diff --git a/packages/CLI11/.all-contributorsrc b/packages/CLI11/.all-contributorsrc
new file mode 100644
index 0000000000000000000000000000000000000000..cb29dab546cf41c298855feed8f3b882621ac22a
--- /dev/null
+++ b/packages/CLI11/.all-contributorsrc
@@ -0,0 +1,348 @@
+{
+  "projectName": "CLI11",
+  "projectOwner": "CLIUtils",
+  "repoType": "github",
+  "repoHost": "https://github.com",
+  "files": [
+    "README.md"
+  ],
+  "imageSize": 100,
+  "commit": true,
+  "commitConvention": "atom",
+  "contributors": [
+    {
+      "login": "henryiii",
+      "name": "Henry Schreiner",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/4616906?v=4",
+      "profile": "http://iscinumpy.gitlab.io",
+      "contributions": [
+        "bug",
+        "doc",
+        "code"
+      ]
+    },
+    {
+      "login": "phlptp",
+      "name": "Philip Top",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/20667153?v=4",
+      "profile": "https://github.com/phlptp",
+      "contributions": [
+        "bug",
+        "doc",
+        "code"
+      ]
+    },
+    {
+      "login": "cbachhuber",
+      "name": "Christoph Bachhuber",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/27212661?v=4",
+      "profile": "https://www.linkedin.com/in/cbachhuber/",
+      "contributions": [
+        "example",
+        "code"
+      ]
+    },
+    {
+      "login": "lambdafu",
+      "name": "Marcus Brinkmann",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/1138455?v=4",
+      "profile": "https://lambdafu.net/",
+      "contributions": [
+        "bug",
+        "code"
+      ]
+    },
+    {
+      "login": "SkyToGround",
+      "name": "Jonas Nilsson",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/58835?v=4",
+      "profile": "https://github.com/SkyToGround",
+      "contributions": [
+        "bug",
+        "code"
+      ]
+    },
+    {
+      "login": "dvj",
+      "name": "Doug Johnston",
+      "avatar_url": "https://avatars2.githubusercontent.com/u/77217?v=4",
+      "profile": "https://github.com/dvj",
+      "contributions": [
+        "bug",
+        "code"
+      ]
+    },
+    {
+      "login": "lczech",
+      "name": "Lucas Czech",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/4741887?v=4",
+      "profile": "http://lucas-czech.de",
+      "contributions": [
+        "bug",
+        "code"
+      ]
+    },
+    {
+      "login": "rafiw",
+      "name": "Rafi Wiener",
+      "avatar_url": "https://avatars3.githubusercontent.com/u/3034707?v=4",
+      "profile": "https://github.com/rafiw",
+      "contributions": [
+        "bug",
+        "code"
+      ]
+    },
+    {
+      "login": "mensinda",
+      "name": "Daniel Mensinger",
+      "avatar_url": "https://avatars3.githubusercontent.com/u/3407462?v=4",
+      "profile": "https://github.com/mensinda",
+      "contributions": [
+        "platform"
+      ]
+    },
+    {
+      "login": "jbriales",
+      "name": "Jesus Briales",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/6850478?v=4",
+      "profile": "https://github.com/jbriales",
+      "contributions": [
+        "code",
+        "bug"
+      ]
+    },
+    {
+      "login": "seanfisk",
+      "name": "Sean Fisk",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/410322?v=4",
+      "profile": "https://seanfisk.com/",
+      "contributions": [
+        "bug",
+        "code"
+      ]
+    },
+    {
+      "login": "fpeng1985",
+      "name": "fpeng1985",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/87981?v=4",
+      "profile": "https://github.com/fpeng1985",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "almikhayl",
+      "name": "almikhayl",
+      "avatar_url": "https://avatars2.githubusercontent.com/u/6747040?v=4",
+      "profile": "https://github.com/almikhayl",
+      "contributions": [
+        "code",
+        "platform"
+      ]
+    },
+    {
+      "login": "andrew-hardin",
+      "name": "Andrew Hardin",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/16496326?v=4",
+      "profile": "https://github.com/andrew-hardin",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "SX91",
+      "name": "Anton",
+      "avatar_url": "https://avatars2.githubusercontent.com/u/754754?v=4",
+      "profile": "https://github.com/SX91",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "helmesjo",
+      "name": "Fred HelmesjΓΆ",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/2501070?v=4",
+      "profile": "https://github.com/helmesjo",
+      "contributions": [
+        "bug",
+        "code"
+      ]
+    },
+    {
+      "login": "skannan89",
+      "name": "Kannan",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/11918764?v=4",
+      "profile": "https://github.com/skannan89",
+      "contributions": [
+        "bug",
+        "code"
+      ]
+    },
+    {
+      "login": "kraj",
+      "name": "Khem Raj",
+      "avatar_url": "https://avatars3.githubusercontent.com/u/465279?v=4",
+      "profile": "http://himvis.com",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "mogigoma",
+      "name": "Mak Kolybabi",
+      "avatar_url": "https://avatars2.githubusercontent.com/u/130862?v=4",
+      "profile": "https://www.mogigoma.com/",
+      "contributions": [
+        "doc"
+      ]
+    },
+    {
+      "login": "msoeken",
+      "name": "Mathias Soeken",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/1998245?v=4",
+      "profile": "http://msoeken.github.io",
+      "contributions": [
+        "doc"
+      ]
+    },
+    {
+      "login": "nathanhourt",
+      "name": "Nathan Hourt",
+      "avatar_url": "https://avatars2.githubusercontent.com/u/271977?v=4",
+      "profile": "https://github.com/nathanhourt",
+      "contributions": [
+        "bug",
+        "code"
+      ]
+    },
+    {
+      "login": "pleroux0",
+      "name": "Paul le Roux",
+      "avatar_url": "https://avatars2.githubusercontent.com/u/39619854?v=4",
+      "profile": "https://github.com/pleroux0",
+      "contributions": [
+        "code",
+        "platform"
+      ]
+    },
+    {
+      "login": "chfast",
+      "name": "PaweΕ‚ Bylica",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/573380?v=4",
+      "profile": "https://github.com/chfast",
+      "contributions": [
+        "platform"
+      ]
+    },
+    {
+      "login": "peterazmanov",
+      "name": "Peter Azmanov",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/15322318?v=4",
+      "profile": "https://github.com/peterazmanov",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "delpinux",
+      "name": "StΓ©phane Del Pino",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/35096584?v=4",
+      "profile": "https://github.com/delpinux",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "metopa",
+      "name": "Viacheslav Kroilov",
+      "avatar_url": "https://avatars2.githubusercontent.com/u/3974178?v=4",
+      "profile": "https://github.com/metopa",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "ChristosT",
+      "name": "christos",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/6725596?v=4",
+      "profile": "http://cs.odu.edu/~ctsolakis",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "deining",
+      "name": "deining",
+      "avatar_url": "https://avatars3.githubusercontent.com/u/18169566?v=4",
+      "profile": "https://github.com/deining",
+      "contributions": [
+        "doc"
+      ]
+    },
+    {
+      "login": "elszon",
+      "name": "elszon",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/2971495?v=4",
+      "profile": "https://github.com/elszon",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "ncihnegn",
+      "name": "ncihnegn",
+      "avatar_url": "https://avatars3.githubusercontent.com/u/12021721?v=4",
+      "profile": "https://github.com/ncihnegn",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "nurelin",
+      "name": "nurelin",
+      "avatar_url": "https://avatars3.githubusercontent.com/u/5276274?v=4",
+      "profile": "https://github.com/nurelin",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "ryan4729",
+      "name": "ryan4729",
+      "avatar_url": "https://avatars3.githubusercontent.com/u/40183301?v=4",
+      "profile": "https://github.com/ryan4729",
+      "contributions": [
+        "test"
+      ]
+    },
+    {
+      "login": "slurps-mad-rips",
+      "name": "Isabella Muerte",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/63051?v=4",
+      "profile": "https://izzys.casa",
+      "contributions": [
+        "platform"
+      ]
+    },
+    {
+      "login": "KOLANICH",
+      "name": "KOLANICH",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/240344?v=4",
+      "profile": "https://github.com/KOLANICH",
+      "contributions": [
+        "platform"
+      ]
+    },
+    {
+      "login": "jgerityneurala",
+      "name": "James Gerity",
+      "avatar_url": "https://avatars2.githubusercontent.com/u/57360646?v=4",
+      "profile": "https://github.com/jgerityneurala",
+      "contributions": [
+        "doc"
+      ]
+    }
+  ],
+  "contributorsPerLine": 7,
+  "skipCi": true
+}
diff --git a/packages/CLI11/.appveyor.yml b/packages/CLI11/.appveyor.yml
index 9d9394713a6bedd3d4c8cf2e9234ea51c8a93e69..9f3365cc4f5d310da21e21648855e0dc4abcf3fa 100644
--- a/packages/CLI11/.appveyor.yml
+++ b/packages/CLI11/.appveyor.yml
@@ -1,4 +1,4 @@
-version: 1.8.0.{build}
+version: 1.9.0.{build}
 
 branches:
   only:
@@ -6,9 +6,12 @@ branches:
 
 install:
   - git submodule update --init --recursive
-  - set PATH=C:\Python36;%PATH%
+  - py -3 --version
+  - set PATH=C:\Python38-x64;C:\Python38-x64\Scripts;%PATH%
   - cmake --version
-  - pip install conan
+  - python --version
+  - python -m pip --version
+  - python -m pip install conan
   - conan user
   - conan --version
 
diff --git a/packages/CLI11/.ci/azure-build.yml b/packages/CLI11/.ci/azure-build.yml
index 234e442d4aa897fa88a2cb5cc858ec85d02c5438..06d60cec91733405a0e03e1c84ef899e59001248 100644
--- a/packages/CLI11/.ci/azure-build.yml
+++ b/packages/CLI11/.ci/azure-build.yml
@@ -2,7 +2,7 @@ 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)
+    cmakeArgs: .. -DCLI11_WARNINGS_AS_ERRORS=ON -DCLI11_SINGLE_FILE=$(cli11.single) -DCMAKE_CXX_STANDARD=$(cli11.std) -DCLI11_SINGLE_FILE_TESTS=$(cli11.single) -DCMAKE_BUILD_TYPE=$(cli11.build_type) $(cli11.options)
   displayName: 'Configure'
 
 - script: cmake --build .
diff --git a/packages/CLI11/.ci/build_doxygen.sh b/packages/CLI11/.ci/build_doxygen.sh
index 5d139a720deedc7d49a829b76a699ce730457d41..4474f696ab76f493077f834873b4a55f5bab0246 100644
--- a/packages/CLI11/.ci/build_doxygen.sh
+++ b/packages/CLI11/.ci/build_doxygen.sh
@@ -3,7 +3,8 @@
 
 set -evx
 
-DOXYGEN_URL="http://doxygen.nl/files/doxygen-1.8.15.src.tar.gz"
+#DOXYGEN_URL="http://doxygen.nl/files/doxygen-1.8.17.src.tar.gz"
+DOXYGEN_URL="https://github.com/doxygen/doxygen/archive/Release_1_8_15.tar.gz"
 cd "${DEPS_DIR}"
 
 if [[ ! -f "${DEPS_DIR}/doxygen/build/bin/doxygen" ]] ; then
diff --git a/packages/CLI11/.ci/make_and_test.sh b/packages/CLI11/.ci/make_and_test.sh
index e326aa4d3cfb24648792bf20c7d38ea21d8b24a5..af8de340f53ac65911ab305c0949aaf793804fde 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_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 .. -DCLI11_WARNINGS_AS_ERRORS=ON -DCLI11_SINGLE_FILE=ON -DCMAKE_CXX_STANDARD=$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/.clang-tidy b/packages/CLI11/.clang-tidy
index dbf7d077ff9b9a2e5b2df7e714e56d7973096083..c9b6458fc13541c63705403ac53cbb1de865a42f 100644
--- a/packages/CLI11/.clang-tidy
+++ b/packages/CLI11/.clang-tidy
@@ -1,8 +1,35 @@
-#Checks: '*,-clang-analyzer-alpha.*'
-#Checks: '-*,google-readability-casting,llvm-namespace-comment,performance-unnecessary-value-param,llvm-include-order,misc-throw-by-value-catch-by-reference,readability-container-size-empty,google-runtime-references,modernize*'
-Checks: '-*,llvm-namespace-comment,readability-container-size-empty,misc-throw-by-value-catch-by-reference,modernize*,google-readability-casting'
+# Checks that will be implemented in future PRs:
+# performance-unnecessary-value-param, hints to ~110 issues. Be careful with implementing the suggested changes of this one, as auto-fixes may break the code
+
+FormatStyle: file
+
+Checks: '
+-*,
+google-*,
+-google-runtime-int,
+-google-runtime-references,
+llvm-include-order,
+llvm-namespace-comment,
+misc-throw-by-value-catch-by-reference,
+modernize*,
+readability-container-size-empty,
+'
+
+WarningsAsErrors: '
+-*,
+google-*,
+-google-runtime-int,
+-google-runtime-references,
+llvm-include-order,
+llvm-namespace-comment,
+misc-throw-by-value-catch-by-reference,
+modernize*,
+readability-container-size-empty,
+'
+
 HeaderFilterRegex: '.*hpp'
+
 CheckOptions:
-- key:             readability-braces-around-statements.ShortStatementLines
-  value:           '1'
+- key:             google-readability-braces-around-statements.ShortStatementLines
+  value:           '3'
 
diff --git a/packages/CLI11/.github/CONTRIBUTING.md b/packages/CLI11/.github/CONTRIBUTING.md
index c97edaa281dec4e1a84e6461c59679f5a1229d7b..fcee45d43671f3facfd2a133e075fcbd3300f757 100644
--- a/packages/CLI11/.github/CONTRIBUTING.md
+++ b/packages/CLI11/.github/CONTRIBUTING.md
@@ -17,7 +17,8 @@ In general, make sure the addition is well thought out and does not increase the
 * Once you make the PR, tests will run to make sure your code works on all supported platforms
 * The test coverage is also measured, and that should remain 100%
 * Formatting should be done with pre-commit, otherwise the format check will not pass. However, it is trivial to apply this to your PR, so don't worry about this check. If you do want to run it, see below.
-* Everything must pass clang-tidy as well, run with `-DCLANG_TIDY_FIX-ON` (make sure you use a single threaded build process!)
+* Everything must pass clang-tidy as well, run with `-DCLI11_CLANG_TIDY=ON` (if you set `-DCLI11_CLANG_TIDY_OPTIONS="-fix"`, make sure you use a single threaded build process, or just build one example target).
+* Your changes must also conform to most of the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) rules checked by [cpplint](https://github.com/cpplint/cpplint). For unused cpplint filters and justifications, see [CPPLINT.cfg](/CPPLINT.cfg).
 
 
 ## Pre-commit
@@ -54,3 +55,26 @@ conan upload "*" -r cli11 --all
 ```
 
 Here I've assumed that the remote is `cli11`.
+
+## For maintainers: remember to add contributions
+
+In a commit to a PR, just add "`@all-contributors please add <username> for <contributions>`" or similar (see <https://allcontributors.org>). Use `code` for code, `bug` if an issue was submitted, `platform` for packaging stuff, and `doc` for documentation updates.
+
+To run locally, do:
+
+```bash
+yarn add --dev all-contributors-cli
+yarn all-contributors add username code,bug
+```
+
+## For maintainers: Making a release
+
+Remember to replace the emoji in the readme, being careful not to replace the ones in all-contributors if any overlap.
+
+Steps:
+* Update changelog if needed
+* Update the version in `.appveyor.yml` and `include/CLI/Version.hpp`.
+* Find and replace in README:
+    * Replace " πŸ†•" and "πŸ†• " with "" (ignores the description line)
+    * Check for `\/\/$` (vi syntax) to catch leftover `// πŸ†•`
+    * Replace "🚧" with "πŸ†•" (manually ignore the description line)
diff --git a/packages/CLI11/.github/actions/cmake_config/Dockerfile b/packages/CLI11/.github/actions/cmake_config/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..81eb9b9965f0deb6e4e252d9cd9c5262a4d118a9
--- /dev/null
+++ b/packages/CLI11/.github/actions/cmake_config/Dockerfile
@@ -0,0 +1,16 @@
+FROM ubuntu:18.04
+
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends \
+        g++=4:7.4.0-1ubuntu2.3 \
+        wget=1.19.4-1ubuntu2.2 \
+        libidn11=1.33-2.1ubuntu1.2 \
+        ca-certificates=20180409 \
+        make=4.1-9.1ubuntu1 \
+        git=1:2.17.1-1ubuntu0.5 \
+ && apt-get clean \
+ && rm -rf /var/lib/apt/lists/*
+
+COPY entrypoint.sh /entrypoint.sh
+
+ENTRYPOINT ["/entrypoint.sh"]
diff --git a/packages/CLI11/.github/actions/cmake_config/action.yml b/packages/CLI11/.github/actions/cmake_config/action.yml
new file mode 100644
index 0000000000000000000000000000000000000000..73ff1661b5a6fe2840339996cf88671bc1053b67
--- /dev/null
+++ b/packages/CLI11/.github/actions/cmake_config/action.yml
@@ -0,0 +1,16 @@
+description: 'Test out a bare bones configuration with a CMake version'
+inputs:
+  version:
+    description: 'The full version of CMake to check'
+    required: true
+  options:
+    description: 'The CMake configuration options'
+    required: false
+    default: ""
+name: 'Configure with CMake'
+runs:
+  using: 'docker'
+  image: 'Dockerfile'
+  args:
+    - ${{ inputs.version }}
+    - ${{ inputs.options }}
diff --git a/packages/CLI11/.github/actions/cmake_config/entrypoint.sh b/packages/CLI11/.github/actions/cmake_config/entrypoint.sh
new file mode 100755
index 0000000000000000000000000000000000000000..42e263ede57b97c868033745cdf4743e9eef0c65
--- /dev/null
+++ b/packages/CLI11/.github/actions/cmake_config/entrypoint.sh
@@ -0,0 +1,23 @@
+#!/bin/bash -l
+
+set -ex
+
+mkdir -p cmake_dir
+mkdir -p build_tmp
+mkdir -p cmake_sources
+rm -rf cmake_dir/* build_tmp/*
+
+v=$1
+fn=cmake-$v-Linux-x86_64.tar.gz
+
+if [ ! -f cmake_souces/$fn ]; then
+    wget -qO cmake_sources/$fn "https://cmake.org/files/v${v%.*}/$fn"
+fi
+
+tar -xzf cmake_sources/$fn --strip-components=1 -C $PWD/cmake_dir
+
+export PATH=$PWD/cmake_dir/bin:$PATH
+
+cmake --version
+
+cd build_tmp && cmake .. $2
diff --git a/packages/CLI11/.github/workflows/build.yml b/packages/CLI11/.github/workflows/build.yml
new file mode 100644
index 0000000000000000000000000000000000000000..35864c098b830f102315b20d45932e28f892a699
--- /dev/null
+++ b/packages/CLI11/.github/workflows/build.yml
@@ -0,0 +1,57 @@
+name: Build
+on:
+  push:
+    branches:
+      - master
+      - v*
+    tags:
+      - "*"
+  pull_request:
+    branches:
+      - master
+
+jobs:
+  single-header:
+    name: Single header
+    runs-on: ubuntu-latest
+    steps:
+
+    - uses: actions/checkout@v1
+      with:
+        submodules: true
+
+    - uses: actions/setup-python@v1
+
+    - name: Make header
+      run: python ./scripts/MakeSingleHeader.py CLI11.hpp
+
+    - name: Prepare CMake config
+      run: cmake -S . -B build
+
+    - name: Make package
+      run: cmake --build build --target package_source
+
+    - name: Copy source packages
+      run: |
+        mkdir -p CLI11-Source
+        cp build/CLI11-*-Source.* CLI11-Source
+        cp build/CLI11-*-Source.* .
+
+    - uses: actions/upload-artifact@v1
+      with:
+        name: CLI11.hpp
+        path: CLI11.hpp
+
+    - uses: actions/upload-artifact@v1
+      with:
+        name: CLI11-Source
+        path: CLI11-Source
+
+    - name: Release
+      uses: softprops/action-gh-release@v1
+      if: startsWith(github.ref, 'refs/tags/')
+      with:
+        files: |
+          CLI11.hpp
+      env:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/packages/CLI11/.github/workflows/tests.yml b/packages/CLI11/.github/workflows/tests.yml
index ac5067405aaf06930f09d299b3f41293b27384da..c3c832bc6e08e3b057dcb01ef8cc2ea7293c14da 100644
--- a/packages/CLI11/.github/workflows/tests.yml
+++ b/packages/CLI11/.github/workflows/tests.yml
@@ -9,24 +9,107 @@ on:
       - master
 
 jobs:
-  formatting:
+  pre-commit:
     name: Formatting
     runs-on: ubuntu-latest
-
     steps:
-      - uses: actions/checkout@v1
-      - uses: actions/setup-python@v1
-        with:
-          python-version: '3.7'
-          architecture: 'x64'
-
-      - name: Install pre-commit
-        run: python -m pip install pre-commit
+    - uses: actions/checkout@v1
+    - uses: actions/setup-python@v1
+    - name: set PY
+      run: echo "::set-env name=PY::$(python --version --version | sha256sum | cut -d' ' -f1)"
+    - uses: actions/cache@v1
+      with:
+        path: ~/.cache/pre-commit
+        key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
+    - uses: pre-commit/action@v1.0.0
 
-      - name: Run pre-commit
-        run: pre-commit run --all
-
-      - name: Display format changes
-        run: git diff --exit-code
-        if: always()
+  cuda-build:
+    name: CUDA build only
+    runs-on: ubuntu-latest
+    container: nvidia/cuda:10.2-devel-ubuntu18.04
+    steps:
+    - uses: actions/checkout@v1
+      with:
+        submodules: true
+    - name: Add wget
+      run: apt-get update && apt-get install -y wget
+    - name: Install Modern CMake
+      run: wget -qO- "https://cmake.org/files/v3.16/cmake-3.16.0-Linux-x86_64.tar.gz" | tar --strip-components=1 -xz -C /usr/local
+    - name: Configure
+      run: cmake -S . -B build -DCLI11_CUDA_TESTS=ON
+    - name: Build
+      run: cmake --build build -j2
 
+  cmake-config:
+    name: CMake config check
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v1
+      with:
+        submodules: true
+    - name: CMake 3.4
+      uses: ./.github/actions/cmake_config
+      with:
+        version: 3.4.3
+    - name: CMake 3.5
+      uses: ./.github/actions/cmake_config
+      with:
+        version: 3.5.2
+      if: success() || failure()
+    - name: CMake 3.6
+      uses: ./.github/actions/cmake_config
+      with:
+        version: 3.6.3
+      if: success() || failure()
+    - name: CMake 3.7
+      uses: ./.github/actions/cmake_config
+      with:
+        version: 3.7.2
+      if: success() || failure()
+    - name: CMake 3.8
+      uses: ./.github/actions/cmake_config
+      with:
+        version: 3.8.2
+      if: success() || failure()
+    - name: CMake 3.9
+      uses: ./.github/actions/cmake_config
+      with:
+        version: 3.9.6
+      if: success() || failure()
+    - name: CMake 3.10
+      uses: ./.github/actions/cmake_config
+      with:
+        version: 3.10.3
+      if: success() || failure()
+    - name: CMake 3.11 (full)
+      uses: ./.github/actions/cmake_config
+      with:
+        version: 3.11.4
+        options: -DCLI11_SANITIZERS=ON -DCLI11_BUILD_EXAMPLES_JSON=ON
+      if: success() || failure()
+    - name: CMake 3.12
+      uses: ./.github/actions/cmake_config
+      with:
+        version: 3.12.4
+      if: success() || failure()
+    - name: CMake 3.13
+      uses: ./.github/actions/cmake_config
+      with:
+        version: 3.13.5
+      if: success() || failure()
+    - name: CMake 3.14
+      uses: ./.github/actions/cmake_config
+      with:
+        version: 3.14.7
+      if: success() || failure()
+    - name: CMake 3.15
+      uses: ./.github/actions/cmake_config
+      with:
+        version: 3.15.6
+      if: success() || failure()
+    - name: CMake 3.16 (full)
+      uses: ./.github/actions/cmake_config
+      with:
+        version: 3.16.2
+        options: -DCLI11_SANITIZERS=ON -DCLI11_BUILD_EXAMPLES_JSON=ON
+      if: success() || failure()
diff --git a/packages/CLI11/.gitignore b/packages/CLI11/.gitignore
index b667eef1d9b140503a1fb2a800e32b62520da902..2a6ef59a35af60bd3b7ae6ec675f2b045fd8dfbb 100644
--- a/packages/CLI11/.gitignore
+++ b/packages/CLI11/.gitignore
@@ -8,3 +8,7 @@ a.out*
 /*.kdev4
 /html/*
 !/meson.build
+
+/node_modules/*
+/package.json
+/yarn.lock
diff --git a/packages/CLI11/.gitmodules b/packages/CLI11/.gitmodules
index e9ec356f3475d5a3c7f043752ed93061180a8c51..6051b7f20049a412a56aa8f664a56a8e35b0278f 100644
--- a/packages/CLI11/.gitmodules
+++ b/packages/CLI11/.gitmodules
@@ -1,9 +1,3 @@
 [submodule "extern/googletest"]
 	path = extern/googletest
 	url = ../../google/googletest.git
-[submodule "extern/sanitizers"]
-	path = extern/sanitizers
-	url = ../../arsenm/sanitizers-cmake
-[submodule "extern/json"]
-	path = extern/json
-	url = ../../nlohmann/json.git
diff --git a/packages/CLI11/.gitrepo b/packages/CLI11/.gitrepo
index 92186b6fd81abdd1a47638fd8708a0f045df4e6a..2568d95d7891ca97c84ce111c9cae420395722ab 100644
--- a/packages/CLI11/.gitrepo
+++ b/packages/CLI11/.gitrepo
@@ -6,7 +6,7 @@
 [subrepo]
 	remote = git@github.com:CLIUtils/CLI11.git
 	branch = master
-	commit = 41d3c967d7e22df534e8584db67f18489694a89b
-	parent = a0ecb7d8ebee7f34714cde913b3d8b358a1fee59
-	cmdver = 0.4.0
+	commit = 34bfa386917c2bed3ffe979580b07064a5586936
+	parent = a0bd81eafb1704b59611b899ceb4aa1e7513bb02
+	cmdver = 0.4.1
 	method = merge
diff --git a/packages/CLI11/.travis.yml b/packages/CLI11/.travis.yml
index 831aa36421956d643f82fd1c8307d1c9fa21e5df..d352aed96d47a9169595813dca8fa8ff2899a69e 100644
--- a/packages/CLI11/.travis.yml
+++ b/packages/CLI11/.travis.yml
@@ -106,15 +106,6 @@ deploy:
   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
-  file: build/include/CLI11.hpp
-  on:
-    repo: CLIUtils/CLI11
-    tags: true
-    condition: "$DEPLOY_MAT = yes"
 
 notifications:
   webhooks:
diff --git a/packages/CLI11/CHANGELOG.md b/packages/CLI11/CHANGELOG.md
index 67840e96b5fab8073628b4b4dff4345c34a84666..b23d69c88bbea6845c1e8c9c2696e3dfa29d08a4 100644
--- a/packages/CLI11/CHANGELOG.md
+++ b/packages/CLI11/CHANGELOG.md
@@ -1,13 +1,47 @@
-## Version 1.9: IN PROGRESS
+## Version 1.9: Config files and cleanup
 
-* The meson build system supported [#299][]
+Config file handling was revamped to fix common issues, and now supports reading [TOML](https://github.com/toml-lang/toml).
+
+Adding options is significantly more powerful with support for things like
+`std::tuple` and `std::array`, including with transforms. Several new
+configuration options were added to facilitate a wider variety of apps.  GCC
+4.7 is no longer supported.
+
+* Config files refactored, supports TOML (may become default output in 2.0) [#362][]
 * 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][]
+* `immediate_callback`, `final_callback`, and `parse_complete_callback` added to support controlling the App callback order [#292][], [#313][]
+* Multiple positional arguments maintain order if `positionals_at_end` is set. [#306][]
+* Pair/tuple/array now supported, and validators indexed to specific components in the objects [#307][], [#310][]
+* Footer callbacks supported [#309][]
+* Subcommands now support needs (including nameless subcommands) [#317][]
+* More flexible type size, more useful `add_complex` [#325][], [#370][]
+* Added new validators `CLI::NonNegativeNumber` and `CLI::PositiveNumber` [#342][]
+* Transform now supports arrays [#349][]
+* Option groups can be hidden [#356][]
+* Add `CLI::deprecate_option` and `CLI::retire_option` functions [#358][]
+* More flexible and safer Option `default_val` [#387][]
 * Backend: Cleaner type traits [#286][]
+* Backend: File checking updates [#341][]
+* Backend: Using pre-commit to format, checked in GitHub Actions [#336][]
+* Backend: Clang-tidy checked again, CMake option now `CL11_CLANG_TIDY` [#390][]
+* Backend: Warning cleanup, more checks from klocwork [#350][], Effective C++ [#354][], clang-tidy [#360][], CUDA NVCC [#365][], cross compile [#373][], sign conversion [#382][], and cpplint [#400][]
+* Docs: CLI11 Tutorial now hosted in the same repository [#304][], [#318][], [#374][]
 * Bugfix: Fixed undefined behavior in `checked_multiply` [#290][]
+* Bugfix: `->check()` was adding the name to the wrong validator [#320][]
 * Bugfix: Resetting config option works properly [#301][]
+* Bugfix: Hidden flags were showing up in error printout [#333][]
+* Bugfix: Enum conversion no longer broken if stream operator added [#348][]
+* Build: The meson build system supported [#299][]
+* Build: GCC 4.7 is no longer supported, due mostly to GoogleTest. GCC 4.8+ is now required. [#160][]
+* Build: Restructured significant portions of CMake build system [#394][]
+
+> ### Converting from CLI11 1.8:
+>
+> * Some deprecated methods dropped
+>     - `add_set*` should be replaced with `->check`/`->transform` and `CLI::IsMember` since 1.8
+>     - `get_defaultval` was replaced by `get_default_str`  in 1.8
+> * The true/false 4th argument to `add_option` is expected to be removed in 2.0, use `->capture_default_str()` since 1.8
 
 [#160]: https://github.com/CLIUtils/CLI11/pull/160
 [#285]: https://github.com/CLIUtils/CLI11/pull/285
@@ -16,6 +50,37 @@
 [#292]: https://github.com/CLIUtils/CLI11/pull/292
 [#299]: https://github.com/CLIUtils/CLI11/pull/299
 [#300]: https://github.com/CLIUtils/CLI11/pull/300
+[#301]: https://github.com/CLIUtils/CLI11/pull/301
+[#304]: https://github.com/CLIUtils/CLI11/pull/304
+[#306]: https://github.com/CLIUtils/CLI11/pull/306
+[#307]: https://github.com/CLIUtils/CLI11/pull/307
+[#309]: https://github.com/CLIUtils/CLI11/pull/309
+[#310]: https://github.com/CLIUtils/CLI11/pull/310
+[#312]: https://github.com/CLIUtils/CLI11/pull/312
+[#313]: https://github.com/CLIUtils/CLI11/pull/313
+[#317]: https://github.com/CLIUtils/CLI11/pull/317
+[#318]: https://github.com/CLIUtils/CLI11/pull/318
+[#320]: https://github.com/CLIUtils/CLI11/pull/320
+[#325]: https://github.com/CLIUtils/CLI11/pull/325
+[#333]: https://github.com/CLIUtils/CLI11/pull/333
+[#336]: https://github.com/CLIUtils/CLI11/pull/336
+[#342]: https://github.com/CLIUtils/CLI11/pull/342
+[#348]: https://github.com/CLIUtils/CLI11/pull/348
+[#349]: https://github.com/CLIUtils/CLI11/pull/349
+[#350]: https://github.com/CLIUtils/CLI11/pull/350
+[#354]: https://github.com/CLIUtils/CLI11/pull/354
+[#356]: https://github.com/CLIUtils/CLI11/pull/356
+[#358]: https://github.com/CLIUtils/CLI11/pull/358
+[#360]: https://github.com/CLIUtils/CLI11/pull/360
+[#362]: https://github.com/CLIUtils/CLI11/pull/362
+[#365]: https://github.com/CLIUtils/CLI11/pull/365
+[#373]: https://github.com/CLIUtils/CLI11/pull/373
+[#374]: https://github.com/CLIUtils/CLI11/pull/374
+[#382]: https://github.com/CLIUtils/CLI11/pull/382
+[#390]: https://github.com/CLIUtils/CLI11/pull/390
+[#394]: https://github.com/CLIUtils/CLI11/pull/394
+[#400]: https://github.com/CLIUtils/CLI11/pull/400
+
 
 ## Version 1.8: Transformers, default strings, and flags
 
@@ -508,4 +573,3 @@ Lots of cleanup and docs additions made it into this release. Parsing is simpler
 ## Version 0.1: First release
 
 First release before major cleanup. Still has make syntax and combiners; very clever syntax but not the best or most commonly expected way to work.
-
diff --git a/packages/CLI11/CLI11.CPack.Description.txt b/packages/CLI11/CLI11.CPack.Description.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1fd074a4b86d84a8d59234648edc8c75533dfaa4
--- /dev/null
+++ b/packages/CLI11/CLI11.CPack.Description.txt
@@ -0,0 +1,2 @@
+CLI11 provides all the features you expect in a powerful command line parser, with a beautiful, minimal syntax and no dependencies beyond C++11. It is header only, and comes in a single file form for easy inclusion in projects. It is easy to use for small projects, but powerful enough for complex command line projects, and can be customized for frameworks.
+
diff --git a/packages/CLI11/CMakeLists.txt b/packages/CLI11/CMakeLists.txt
index ad574a2108bfdada67e6f424657c61faa4ba8dbe..03398433e4a4eff1102d09221f3402997b0cba66 100644
--- a/packages/CLI11/CMakeLists.txt
+++ b/packages/CLI11/CMakeLists.txt
@@ -2,13 +2,14 @@ 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.14) version
+# Make sure users don't get warnings on a tested (3.4 to 3.16) version
 # of CMake. For most of the policies, the new version is better (hence the change).
-# 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)
+# We don't use the 3.4...3.16 syntax because of a bug in an older MSVC's
+# built-in and modified CMake 3.11
+if(${CMAKE_VERSION} VERSION_LESS 3.16)
     cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
 else()
-    cmake_policy(VERSION 3.14)
+    cmake_policy(VERSION 3.16)
 endif()
 
 set(VERSION_REGEX "#define CLI11_VERSION[ \t]+\"(.+)\"")
@@ -23,199 +24,262 @@ 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
+# Print the version number of CMake if this is 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()
+    message(STATUS "CMake ${CMAKE_VERSION}")
+endif()
+
+include(CMakeDependentOption)
+include(GNUInstallDirs)
+include(CTest)
+
+if(NOT CMAKE_VERSION VERSION_LESS 3.11)
+    include(FetchContent)
+endif()
+
+find_package(Doxygen)
+
+list(APPEND force-libcxx "CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\"")
+list(APPEND force-libcxx "CMAKE_SYSTEM_NAME STREQUAL \"Linux\"")
+list(APPEND force-libcxx "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME")
+
+list(APPEND build-docs "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME")
+list(APPEND build-docs "NOT CMAKE_VERSION VERSION_LESS 3.11")
+list(APPEND build-docs "Doxygen_FOUND")
+list(APPEND build-docs "EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/docs")
+
+option(CLI11_WARNINGS_AS_ERRORS "Turn all warnings into errors (for CI)")
+option(CLI11_SINGLE_FILE "Generate a single header file")
+cmake_dependent_option(CLI11_SANITIZERS
+    "Download the sanatizers CMake config" OFF
+    "NOT CMAKE_VERSION VERSION_LESS 3.11" OFF)
+
+cmake_dependent_option(CLI11_BUILD_DOCS
+    "Build CLI11 documentation" ON
+    "${build-docs}" OFF)
+
+cmake_dependent_option(CLI11_BUILD_TESTS
+    "Build CLI11 tests" ON
+    "BUILD_TESTING;CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)
+
+cmake_dependent_option(CLI11_BUILD_EXAMPLES
+    "Build CLI11 examples" ON
+    "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME;EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/examples" OFF)
+
+cmake_dependent_option(CLI11_BUILD_EXAMPLES_JSON
+    "Build CLI11 json example" OFF
+    "CLI11_BUILD_EXAMPLES;NOT CMAKE_VERSION VERSION_LESS 3.11" OFF)
 
-    set(CUR_PROJ ON)
-    set(CMAKE_CXX_STANDARD ${CLI11_CXX_STD})
+cmake_dependent_option(CLI11_SINGLE_FILE_TESTS
+    "Duplicate all the tests for a single file build" OFF
+    "BUILD_TESTING;CLI11_SINGLE_FILE" OFF)
+
+cmake_dependent_option(CLI11_INSTALL
+    "Install the CLI11 folder to include during install process" ON
+    "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)
+
+cmake_dependent_option(CLI11_FORCE_LIBCXX
+    "Force clang to use libc++ instead of libstdc++ (Linux only)" OFF
+    "${force-libcxx}" OFF)
+
+cmake_dependent_option(CLI11_CUDA_TESTS
+    "Build the tests with NVCC to check for warnings there - requires CMake 3.9+" OFF
+    "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)
+
+cmake_dependent_option(CLI11_CLANG_TIDY
+    "Look for and use Clang-Tidy" OFF
+    "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME;NOT CMAKE_VERSION VERSION_LESS 3.6" OFF)
+set(CLI11_CLANG_TIDY_OPTIONS "" CACHE STRING "Clang tidy options, such as -fix, simicolon separated")
+
+if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND NOT DEFINED CMAKE_CXX_STANDARD)
+    set(CMAKE_CXX_STANDARD 11)
+endif()
+
+if(NOT DEFINED CMAKE_CXX_EXTENSIONS)
     set(CMAKE_CXX_EXTENSIONS OFF)
+endif()
+
+if(NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED)
     set(CMAKE_CXX_STANDARD_REQUIRED ON)
+endif()
 
-    option(CLI11_WARNINGS_AS_ERRORS "Turn all warnings into errors (for CI)")
 
-    # Be moderately paranoid with flags
-    if(MSVC)
-        target_compile_options(CLI11_warnings INTERFACE "/W4")
-        if(CLI11_WARNINGS_AS_ERRORS)
-            target_compile_options(CLI11_warnings INTERFACE "/WX")
-        endif()
-    else()
-        target_compile_options(CLI11_warnings INTERFACE -Wall -Wextra -pedantic -Wshadow)
-        if(CLI11_WARNINGS_AS_ERRORS)
-            target_compile_options(CLI11_warnings INTERFACE -Werror)
-        endif()
-    endif()
+# Allow IDE's to group targets into folders
+if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
+    set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+endif()
 
-    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(
-            CLANG_TIDY_EXE
-            NAMES "clang-tidy"
-            DOC "Path to clang-tidy executable"
-        )
+if(CMAKE_VERSION VERSION_LESS 3.10)
+    message(STATUS "CMake 3.10+ adds Doxygen support. Update CMake to build documentation")
+elseif(NOT Doxygen_FOUND)
+    message(STATUS "Doxygen not found, building docs has been disabled")
+endif()
 
-        if(CLANG_TIDY_EXE)
-            if(CLANG_TIDY_FIX)
-                set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "-fix")
-            else()
-                set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}")
-            endif()
-        endif()
-    endif()
+# Special target that adds warnings. Is not exported.
+add_library(CLI11_warnings INTERFACE)
 
-    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)
+set(unix-warnings -Wall -Wextra -pedantic -Wshadow -Wsign-conversion -Wswitch-enum)
+
+# Buggy in GCC 4.8
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
+    list(APPEND unix-warnings -Weffc++)
 endif()
 
-# Allow dependent options
-include(CMakeDependentOption)
+target_compile_options(CLI11_warnings
+   INTERFACE
+     $<$<BOOL:${CLI11_FORCE_LIBCXX}>:-stdlib=libc++>
+     $<$<CXX_COMPILER_ID:MSVC>:/W4 $<$<BOOL:${CLI11_WARNINGS_AS_ERRORS}>:/WX>>
+     $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:${unix-warnings} $<$<BOOL:${CLI11_WARNINGS_AS_ERRORS}>:-Werror>>)
 
-# Allow IDE's to group targets into folders
-set_property(GLOBAL PROPERTY USE_FOLDERS ON)
 
-file(GLOB CLI11_headers "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/*")
-# To see in IDE, must be listed for target
+ if(NOT CMAKE_VERSION VERSION_LESS 3.13)
+     target_link_options(CLI11_warnings
+         INTERFACE
+         $<$<BOOL:${CLI11_FORCE_LIBCXX}>:-stdlib=libc++>)
+ endif()
 
+
+# Allow IDE's to group targets into folders
 add_library(CLI11 INTERFACE)
+add_library(CLI11::CLI11 ALIAS CLI11) # for add_subdirectory calls
 
 # Duplicated because CMake adds the current source dir if you don't.
 target_include_directories(CLI11 INTERFACE
      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
      $<INSTALL_INTERFACE:include>)
 
-# Make add_subdirectory work like find_package
-add_library(CLI11::CLI11 ALIAS CLI11)
 
-option(CLI11_INSTALL "Install the CLI11 folder to include during install process" ${CUR_PROJ})
+# To see in IDE, headers must be listed for target
+set(header-patterns "${PROJECT_SOURCE_DIR}/include/CLI/*")
+if(NOT CMAKE_VERSION VERSION_LESS 3.12)
+  list(INSERT header-patterns 0 CONFIGURE_DEPENDS)
+endif()
+
+file(GLOB CLI11_headers ${header-patterns})
+
+# Allow tests to be run on CUDA
+ if(CLI11_CUDA_TESTS)
+     enable_language(CUDA)
+
+     # Print out warning and error numbers
+     set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcudafe --display_error_number")
+ endif()
+
+
+# Prepare Clang-Tidy
+if(CLI11_CLANG_TIDY)
+    find_program(
+        CLANG_TIDY_EXE
+        NAMES "clang-tidy"
+        DOC "Path to clang-tidy executable"
+        REQUIRED
+        )
+
+    set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" ${CLI11_CLANG_TIDY_OPTIONS})
+endif()
+
 
 # This folder should be installed
 if(CLI11_INSTALL)
-    install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/CLI DESTINATION include)
+    install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/
+        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
 
     # Make an export target
-    install(TARGETS CLI11
-            EXPORT CLI11Targets)
-endif()
+    install(TARGETS CLI11 EXPORT CLI11Targets)
 
-# Use find_package on the installed package
-# Since we have no custom code, we can directly write this
-# to Config.cmake (otherwise we'd have a custom config and would
-# import Targets.cmake
+    # Use find_package on the installed package
+    # Since we have no custom code, we can directly write this
+    # to Config.cmake (otherwise we'd have a custom config and would
+    # import Targets.cmake
 
-# Add the version in a CMake readable way
-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CLI11ConfigVersion.cmake.in"
-  "${CMAKE_CURRENT_BINARY_DIR}/CLI11ConfigVersion.cmake" @ONLY)
+    # Add the version in a CMake readable way
+    configure_file("cmake/CLI11ConfigVersion.cmake.in"
+       "CLI11ConfigVersion.cmake" @ONLY)
 
-# These installs only make sense for a local project
-if(CUR_PROJ)
     # Make version available in the install
-    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/CLI11ConfigVersion.cmake"
-            DESTINATION lib/cmake/CLI11)
+    install(FILES "${PROJECT_BINARY_DIR}/CLI11ConfigVersion.cmake"
+        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/CLI11)
 
     # Install the export target as a file
     install(EXPORT CLI11Targets
-            FILE CLI11Config.cmake
-            NAMESPACE CLI11::
-            DESTINATION lib/cmake/CLI11)
+        FILE CLI11Config.cmake
+        NAMESPACE CLI11::
+        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/CLI11)
 
     # Use find_package on the installed package
     export(TARGETS CLI11
-           NAMESPACE CLI11::
-           FILE CLI11Targets.cmake)
+        NAMESPACE CLI11::
+        FILE CLI11Targets.cmake)
 
     # Register in the user cmake package registry
     export(PACKAGE CLI11)
 endif()
 
-option(CLI11_SINGLE_FILE "Generate a single header file" OFF)
-
 if(CLI11_SINGLE_FILE)
-# Single file test
+    # Single file test
     if(CMAKE_VERSION VERSION_LESS 3.12)
         find_package(PythonInterp REQUIRED)
-        set(Python_VERSION ${PYTHON_VERSION_STRING})
-        set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}")
+        add_executable(Python::Interpreter IMPORTED)
+        set_target_properties(Python::Interpreter
+            PROPERTIES
+            IMPORTED_LOCATION "${PYTHON_EXECUTABLE}"
+            VERSION "${PYTHON_VERSION_STRING}")
     else()
-        find_package(Python REQUIRED)
+        find_package(Python COMPONENTS Interpreter REQUIRED)
     endif()
 
     file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include")
     add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
-        COMMAND "${Python_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/MakeSingleHeader.py" "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
-        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp" ${CLI11_headers}
-        )
-    add_custom_target(generate_cli_single_file ALL
+        COMMAND Python::Interpreter
+        "${CMAKE_CURRENT_SOURCE_DIR}/scripts/MakeSingleHeader.py"
+        "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
+        DEPENDS
+        "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp"
+        ${CLI11_headers})
+    add_custom_target(CLI11-generate-single-file ALL
         DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp")
-    set_target_properties(generate_cli_single_file
-                          PROPERTIES FOLDER "Scripts")
-    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp DESTINATION include)
+    set_property(TARGET CLI11-generate-single-file PROPERTY FOLDER "Scripts")
+    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp
+        DESTINATION include)
     add_library(CLI11_SINGLE INTERFACE)
     target_link_libraries(CLI11_SINGLE INTERFACE CLI11)
-    add_dependencies(CLI11_SINGLE generate_cli_single_file)
+    add_dependencies(CLI11_SINGLE CLI11-generate-single-file)
     target_compile_definitions(CLI11_SINGLE INTERFACE -DCLI11_SINGLE_FILE)
-    target_include_directories(CLI11_SINGLE INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/include/")
+    target_include_directories(CLI11_SINGLE INTERFACE
+        $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/>
+        $<INSTALL_INTERFACE:include>)
 endif()
 
-cmake_dependent_option(CLI11_SINGLE_FILE_TESTS
-    "Duplicate all the tests for a single file build"
-    OFF
-    "CLI11_SINGLE_FILE"
-    OFF)
 
+if(CLI11_BUILD_TESTS)
+  add_subdirectory(tests)
+endif()
 
-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)
+if(CLI11_BUILD_EXAMPLES)
+  add_subdirectory(examples)
 endif()
 
-if(CLI11_TESTING_INTERNAL)
-    enable_testing()
-    add_subdirectory(tests)
+if(CLI11_BUILD_DOCS)
+  add_subdirectory(docs)
 endif()
 
-cmake_dependent_option(CLI11_EXAMPLES "Build the examples" ON "CUR_PROJ" OFF)
-if(CLI11_EXAMPLES)
-    add_subdirectory(examples)
+# From a build system, this might not be included.
+if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/book")
+    add_subdirectory(book)
 endif()
 
 # Packaging support
 set(CPACK_PACKAGE_VENDOR "github.com/CLIUtils/CLI11")
-set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Command line interface")
-set(CPACK_PACKAGE_VERSION_MAJOR ${CLI11_VERSION_MAJOR})
-set(CPACK_PACKAGE_VERSION_MINOR ${CLI11_VERSION_MINOR})
-set(CPACK_PACKAGE_VERSION_PATCH ${CLI11_VERSION_PATCH})
+set(CPACK_PACKAGE_CONTACT "https://${CPACK_PACKAGE_VENDOR}")
+set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) # Automatic in CMake 3.12+
+set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) # Automatic in CMake 3.12+
+set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) # Automatic in CMake 3.12+
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Command line parser with simple and intuitive interface")
 set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
 set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
+set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CLI11.CPack.Description.txt")
 set(CPACK_SOURCE_GENERATOR "TGZ;ZIP")
+
 # CPack collects *everything* except what's listed here.
 set(CPACK_SOURCE_IGNORE_FILES
     /.git
@@ -224,7 +288,22 @@ set(CPACK_SOURCE_IGNORE_FILES
     /\\\\.DS_Store
     /.*\\\\.egg-info
     /var
-    /Pipfile.*$
+    /azure-pipelines.yml
+    /.ci
+    /docs
+    /examples
+    /test_package
+    /book
+    /.travis.yml
+    .swp
+    /.all-contributorsrc
+    /.appveyor.yml
+    /.pre-commit.*yaml
 )
+
+set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "all")
+set(CPACK_DEBIAN_COMPRESSION_TYPE "xz")
+set(CPACK_DEBIAN_PACKAGE_NAME "libcli11-dev")
+
 include(CPack)
 
diff --git a/packages/CLI11/CPPLINT.cfg b/packages/CLI11/CPPLINT.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c55a5f24665ce84d7f98548eb0644b3270ba83ae
--- /dev/null
+++ b/packages/CLI11/CPPLINT.cfg
@@ -0,0 +1,13 @@
+set noparent
+linelength=120  # As in .clang-format
+
+# Non-used filters
+filter=-build/include_order  # Requires unusual include order that encourages creating not self-contained headers
+filter=-readability/nolint  # Conficts with clang-tidy
+filter=-runtime/references  # Requires fundamental change of API, don't see need for this
+filter=-whitespace/blank_line  # Unnecessarily strict with blank lines that otherwise help with readability
+filter=-whitespace/parens,-whitespace/braces  # Conflict with clang-format
+
+# Filters to be included in future
+filter=-whitespace/indent,-whitespace/comments,-readability/braces
+
diff --git a/packages/CLI11/README.md b/packages/CLI11/README.md
index bbaa21badbcf05e6bbd22227513377fe3b25adb0..7891fa98a64cba4d7f10fa62cc6ea914cf44e20e 100644
--- a/packages/CLI11/README.md
+++ b/packages/CLI11/README.md
@@ -5,6 +5,7 @@
 [![Build Status Linux and macOS][travis-badge]][travis]
 [![Build Status Windows][appveyor-badge]][appveyor]
 [![Build Status Azure][azure-badge]][azure]
+[![Actions Status][actions-badge]][actions-link]
 [![Code Coverage][codecov-badge]][codecov]
 [![Codacy Badge][codacy-badge]][codacy-link]
 [![Join the chat at https://gitter.im/CLI11gitter/Lobby][gitter-badge]][gitter]
@@ -12,7 +13,7 @@
 [![Latest release][releases-badge]][github releases]
 [![DOI][doi-badge]][doi-link]
 [![Conan.io][conan-badge]][conan-link]
-[![Try CLI11 1.8 online][wandbox-badge]][wandbox-link]
+[![Try CLI11 1.9 online][wandbox-badge]][wandbox-link]
 
 [What's new](./CHANGELOG.md) β€’
 [Documentation][gitbook] β€’
@@ -33,15 +34,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)
@@ -62,10 +63,10 @@ Features that were added in the last released major version are marked with "
 ### Introduction
 
 CLI11 provides all the features you expect in a powerful command line parser, with a beautiful, minimal syntax and no dependencies beyond C++11. It is header only, and comes in a single file form for easy inclusion in projects. It is easy to use for small projects, but powerful enough for complex command line projects, and can be customized for frameworks.
-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.
+It is tested on [Travis][], [AppVeyor][], [Azure][], and [GitHub Actions][actions-link], and is used by 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?
 
@@ -82,7 +83,7 @@ An acceptable CLI parser library should be all of the following:
 -   Easy to execute, with help, parse errors, etc. providing correct exit and details.
 -   Easy to extend as part of a framework that provides "applications" to users.
 -   Usable subcommand syntax, with support for multiple subcommands, nested subcommands, option groups, and optional fallthrough (explained later).
--   Ability to add a configuration file (`ini` format), and produce it as well.
+-   Ability to add a configuration file (`ini` or `TOML`πŸ†• format), and produce it as well.
 -   Produce real values that can be used directly in code, not something you have pay compute time to look up, for HPC applications.
 -   Work with standard types, simple custom types, and extensible to exotic types.
 -   Permissively licensed.
@@ -105,13 +106,13 @@ After I wrote this, I also found the following libraries:
 | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
 | [GFlags][]              | The Google Commandline Flags library. Uses macros heavily, and is limited in scope, missing things like subcommands. It provides a simple syntax and supports config files/env vars. |
 | [GetOpt][]              | Very limited C solution with long, convoluted syntax. Does not support much of anything, like help generation. Always available on UNIX, though (but in different flavors).          |
-| [ProgramOptions.hxx][]  | Interesting library, less powerful and no subcommands. Nice callback system.                                                                                                          |
+| [ProgramOptions.hxx][]  | Interesting library, less powerful and no subcommands. Nice callback system.                                                                                                         |
 | [Args][]                | Also interesting, and supports subcommands. I like the optional-like design, but CLI11 is cleaner and provides direct value access, and is less verbose.                             |
 | [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.  |
-| [argparse][] | C++17 single file argument parser. Design seems similar to CLI11 in some ways. |
+| [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. The author has several other interesting projects.                                                    |
 
 See [Awesome C++][] for a less-biased list of parsers. You can also find other single file libraries at [Single file libs][].
 
@@ -191,18 +192,18 @@ 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 or that takes an int🚧, double🚧, or string in a constructor. Also allowed are tuples🚧,std::array🚧 or std::pair🚧.
+               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)>, // πŸ†• type can be any type supported by add_option
+               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
+// πŸ†• 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="")
@@ -212,39 +213,26 @@ 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 πŸ†• any singular object with a defined conversion from a string like add_option
+             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, // πŸ†•
-             function <void(int64_t count)>,
+app.add_flag_function(option_name,
+             function <void(std::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); // πŸ†•
-
-// ⚠️  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="")
--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
+Option_group *app.add_option_group(name,description);
 ```
 
-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.
+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
+πŸ†• 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);
@@ -257,9 +245,9 @@ 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.
+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.
+Types such as (std or boost) `optional<int>`, `optional<double>`, and `optional<string>` are supported directly, other optional types can be added using the two parameter template.  See [CLI11 Advanced Topics/Custom Converters][] for information on how this could be done and how you can add your own converters for additional types.
 
 Vector types can also be used in the two parameter template overload
 ```
@@ -270,10 +258,10 @@ would load a vector of doubles but ensure all values can be represented as integ
 
 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
@@ -285,7 +273,7 @@ all the given arguments and return the result.  This can be modified if needed b
 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.
@@ -317,19 +305,21 @@ Before parsing, you can set the following options:
 -   `->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.
+-   `->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.
+-   `->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`.
+    `->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`.
+-   `default_str(string)`:  Set the default string directly.  This string will also be used as a default value if no arguments are passed and the value is requested.
+-   `default_val(value)`: πŸ†• Generate the default string from a value and validate that the value is also valid.  For options that assign directly to a value type the value in that type is also updated.  Value must be convertible to a string(one of known types or a stream operator).
 
 
 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.
@@ -342,7 +332,7 @@ 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)
 
@@ -352,14 +342,14 @@ If `allow_windows_style_options()` is specified in the application or subcommand
 -   `/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.
+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
@@ -370,20 +360,21 @@ 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::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::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 than 0
+-   `CLI::NonNegativeNumber`: 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
 
@@ -392,7 +383,7 @@ These Validators can be used by simply passing the name into the `check` or `tra
 ->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));
@@ -404,12 +395,12 @@ will produce a check to ensure a value is between 0 and 10 or 20 and 30.
 ->check(!CLI::PositiveNumber);
 ```
 
-will produce a check for a number less than 0.
+will produce a check for a number less than or equal to 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`:
@@ -419,7 +410,7 @@ of `IsMember`:
 -   `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.
+-   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`:
@@ -432,11 +423,11 @@ of `Transformer`:
 
 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.  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.
+`.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));
@@ -455,7 +446,7 @@ The check can later be activated through
 opt->get_validator("range")->active();
 ```
 
-##### Custom Validators πŸ†•
+##### Custom Validators
 
 A validator object with a custom function can be created via
 
@@ -471,7 +462,7 @@ 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
 
@@ -481,7 +472,7 @@ 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 🚧
+or πŸ†•
 
 ```cpp
 opt->get_validator(index);
@@ -489,19 +480,19 @@ 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_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.
+-   `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(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
 
@@ -517,9 +508,9 @@ All `App`s have a `get_subcommands()` method, which returns a list of pointers t
 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.
+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.
+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
@@ -529,61 +520,62 @@ There are several options that are supported on the main app and subcommands and
 -   `.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`.
--   `.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.
--   `.needs(option_or_subcommand)`: 🚧 If given an option pointer or pointer to another subcommand, the subcommands will require the given option to have been given before this subcommand is validated which occurs prior to execution of any callback or after parsing is completed.
--   `.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.
+-   `.fallthrough()`: Allow extra unmatched options and positionals to "fall through" and be matched on a parent option. Subcommands always are allowed to "fall through" as in they will first attempt to match on the current subcommand and if they fail will progressively check parents for matching subcommands.
+-   `.configurable()`: πŸ†• Allow the subcommand to be triggered from a configuration file. By default subcommand options in a configuration file do not trigger a subcommand but will just update default values.
+-   `.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.
+-   `.needs(option_or_subcommand)`: πŸ†• If given an option pointer or pointer to another subcommand, the subcommands will require the given option to have been given before this subcommand is validated which occurs prior to execution of any callback or after parsing is completed.
+-   `.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.
 -   `.get_description()`: Access the description.
--   `.alias(str)`:🚧 set an alias for the subcommand, this allows subcommands to be called by more than one name.
+-   `.alias(str)`: πŸ†• set an alias for the subcommand, this allows subcommands to be called by more than one name.
 -   `.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 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.
+-   `.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(std::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.
+-   `.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. The operation will throw an exception if the option name is not valid.
+-   `[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 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.
+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_group`s 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
@@ -615,14 +607,14 @@ program --opt1 opt1_val  sub1 --sub1opt --sub1optb val sub2 --sub2opt sub1 --sub
 A subcommand is considered terminated when one of the following conditions are met.
 1. There are no more arguments to process
 2. Another subcommand is encountered that would not fit in an optional slot of the subcommand
-3. The positional_mark(`--`) is encountered and there are no available positional slots in the subcommand.
-4. The subcommand_terminator mark(`++`) is encountered
+3. The `positional_mark` (`--`) is encountered and there are no available positional slots in the subcommand.
+4. The `subcommand_terminator` mark (`++`) is encountered
 
 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
 
@@ -630,7 +622,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 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
+Will create an option group, and return a pointer to it. The argument for `description` is optional and can be omitted.  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);
@@ -649,15 +641,34 @@ 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.
 
-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 functions to allow the use of options/subcommands from one group to trigger another group on or off.
 
 ```cpp
 CLI::TriggerOn(group1_pointer, triggered_group);
 CLI::TriggerOff(group2_pointer, disabled_group);
 ```
 
-These functions make use of `preparse_callback`, `enabled_by_default()` and `disabled_by_default`.  The triggered group may be a vector of group pointers.  These methods should only be used once per group and will override any previous use of the underlying functions.  More complex arrangements can be accomplished using similar methodology with a custom preparse_callback function that does more.
+These functions make use of `preparse_callback`, `enabled_by_default()` and `disabled_by_default`.  The triggered group may be a vector of group pointers.  These methods should only be used once per group and will override any previous use of the underlying functions.  More complex arrangements can be accomplished using similar methodology with a custom `preparse_callback` function that does more.
+
+Additional helper functions `deprecate_option` πŸ†• and `retire_option` πŸ†• are available to deprecate or retire options
+```cpp
+CLI::deprecate_option(option *, replacement_name="");
+CLI::deprecate_option(App,option_name,replacement_name="");
+```
+will specify that the option is deprecated which will display a message in the help and a warning on first usage.  Deprecated options function normally but will add a message in the help and display a warning on first use.
+
+```cpp
+CLI::retire_option(App,option *);
+CLI::retire_option(App,option_name);
+```
+will create an option that does nothing by default and will display a warning on first usage that the option is retired and has no effect.  If the option exists it is replaces with a dummy option that takes the same arguments.
+
+If an empty string is passed the option group name the entire group will be hidden in the help results.  For example.
 
+```cpp
+auto hidden_group=app.add_option_group("");
+```
+will create a group such that no options in that group are displayed in the help string.
 
 ### Configuration file
 
@@ -668,7 +679,7 @@ app.set_config(option_name="",
                required=false)
 ```
 
-If this is called with no arguments, it will remove the configuration file option (like `set_help_flag`). Setting a configuration option is special. If it is present, it will be read along with the normal command line arguments. The file will be read if it exists, and does not throw an error unless `required` is `true`. Configuration files are in `ini` format by default (other formats can be added by an adept user). An example of a file:
+If this is called with no arguments, it will remove the configuration file option (like `set_help_flag`). Setting a configuration option is special. If it is present, it will be read along with the normal command line arguments. The file will be read if it exists, and does not throw an error unless `required` is `true`. Configuration files are in `ini` format by default,  The reader can also accept many files in [TOML] format πŸ†•.  (other formats can be added by an adept user, some variations are available through customization points in the default formatter). An example of a file:
 
 ```ini
 ; Comments are supported, using a ;
@@ -684,17 +695,32 @@ str_vector = "one" "two" "and three"
 in_subcommand = Wow
 sub.subcommand = true
 ```
+ or equivalently in TOML πŸ†•
+```toml
+# Comments are supported, using a #
+# The default section is [default], case insensitive
 
-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.
+value = 1
+str = "A string"
+vector = [1,2,3]
+str_vector = ["one","two","and three"]
+
+# Sections 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`, `enable`; or `false`, `off`, `0`, `no`, `disable` (case insensitive). Sections (and `.` separated names) are treated as subcommands (note: this does not necessarily mean that subcommand was passed, it just sets the "defaults"). You cannot set positional-only arguments.  πŸ†• Subcommands can be triggered from config files if the `configurable` flag was set on the subcommand.  Then use `[subcommand]` notation will trigger a subcommand and cause it to act as if it were on 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.
+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.  See [Config files](https://cliutils.github.io/CLI11/book/chapters/config.html) for some additional details.
 
 ### 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.
 
-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();
@@ -804,6 +830,7 @@ Several short examples of different features are included in the repository. A b
  - [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)
+ - [enum_ostream](https://github.com/CLIUtils/CLI11/blob/master/examples/enum_ostream.cpp):  In addition to the contents of example enum.cpp, this example shows how a custom ostream operator overrides CLI11's enum streaming.
  - [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.
@@ -825,35 +852,69 @@ Several short examples of different features are included in the repository. A b
 
 ## 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).
+To contribute, open an [issue][github issues] or [pull request][github pull requests] on GitHub, or ask a question on [gitter][]. There is also a short note to contributors [here](./.github/CONTRIBUTING.md).
 This readme roughly follows the [Standard Readme Style][] and includes a mention of almost every feature of the library. More complex features are documented in more detail in the [CLI11 tutorial GitBook][gitbook].
 
-This project was created by [Henry Schreiner](https://github.com/henryiii).
-Significant features and/or improvements to the code were contributed by:
-
--   [Anton](https://github.com/SX91)
--   [Doug Johnston](https://github.com/dvj)
--   [Jonas Nilsson](https://github.com/SkyToGround)
--   [Lucas Czech](https://github.com/lczech)
--   [Marcus Brinkmann](https://github.com/lambdafu)
--   [Mathias Soeken](https://github.com/msoeken)
--   [Nathan Hourt](https://github.com/nathanhourt)
--   [Sean Fisk](https://github.com/seanfisk)
--   [StΓ©phane Del Pino](https://github.com/delpinux)
--   [Mak Kolybabi](https://github.com/mogigoma)
--   [PaweΕ‚ Bylica](https://github.com/chfast)
--   [Philip Top](https://github.com/phlptp) <!-- Major features in 1.7 and 1.8 -->
--   [almikhayl](https://github.com/almikhayl)
--   [nurelin](https://github.com/nurelin) <!-- help_all in message -->
--   [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 -->
-
+This project was created by [Henry Schreiner](https://github.com/henryiii) and major features were added by  [Philip Top](https://github.com/phlptp). Special thanks to all the contributors ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
+
+
+<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
+<!-- prettier-ignore-start -->
+<!-- markdownlint-disable -->
+<table>
+  <tr>
+    <td align="center"><a href="http://iscinumpy.gitlab.io"><img src="https://avatars1.githubusercontent.com/u/4616906?v=4" width="100px;" alt=""/><br /><sub><b>Henry Schreiner</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Ahenryiii" title="Bug reports">πŸ›</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=henryiii" title="Documentation">πŸ“–</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=henryiii" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/phlptp"><img src="https://avatars0.githubusercontent.com/u/20667153?v=4" width="100px;" alt=""/><br /><sub><b>Philip Top</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Aphlptp" title="Bug reports">πŸ›</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=phlptp" title="Documentation">πŸ“–</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=phlptp" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://www.linkedin.com/in/cbachhuber/"><img src="https://avatars0.githubusercontent.com/u/27212661?v=4" width="100px;" alt=""/><br /><sub><b>Christoph Bachhuber</b></sub></a><br /><a href="#example-cbachhuber" title="Examples">πŸ’‘</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=cbachhuber" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://lambdafu.net/"><img src="https://avatars1.githubusercontent.com/u/1138455?v=4" width="100px;" alt=""/><br /><sub><b>Marcus Brinkmann</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Alambdafu" title="Bug reports">πŸ›</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=lambdafu" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/SkyToGround"><img src="https://avatars1.githubusercontent.com/u/58835?v=4" width="100px;" alt=""/><br /><sub><b>Jonas Nilsson</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3ASkyToGround" title="Bug reports">πŸ›</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=SkyToGround" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/dvj"><img src="https://avatars2.githubusercontent.com/u/77217?v=4" width="100px;" alt=""/><br /><sub><b>Doug Johnston</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Advj" title="Bug reports">πŸ›</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=dvj" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="http://lucas-czech.de"><img src="https://avatars0.githubusercontent.com/u/4741887?v=4" width="100px;" alt=""/><br /><sub><b>Lucas Czech</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Alczech" title="Bug reports">πŸ›</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=lczech" title="Code">πŸ’»</a></td>
+  </tr>
+  <tr>
+    <td align="center"><a href="https://github.com/rafiw"><img src="https://avatars3.githubusercontent.com/u/3034707?v=4" width="100px;" alt=""/><br /><sub><b>Rafi Wiener</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Arafiw" title="Bug reports">πŸ›</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=rafiw" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/mensinda"><img src="https://avatars3.githubusercontent.com/u/3407462?v=4" width="100px;" alt=""/><br /><sub><b>Daniel Mensinger</b></sub></a><br /><a href="#platform-mensinda" title="Packaging/porting to new platform">πŸ“¦</a></td>
+    <td align="center"><a href="https://github.com/jbriales"><img src="https://avatars1.githubusercontent.com/u/6850478?v=4" width="100px;" alt=""/><br /><sub><b>Jesus Briales</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=jbriales" title="Code">πŸ’»</a> <a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Ajbriales" title="Bug reports">πŸ›</a></td>
+    <td align="center"><a href="https://seanfisk.com/"><img src="https://avatars0.githubusercontent.com/u/410322?v=4" width="100px;" alt=""/><br /><sub><b>Sean Fisk</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Aseanfisk" title="Bug reports">πŸ›</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=seanfisk" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/fpeng1985"><img src="https://avatars1.githubusercontent.com/u/87981?v=4" width="100px;" alt=""/><br /><sub><b>fpeng1985</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=fpeng1985" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/almikhayl"><img src="https://avatars2.githubusercontent.com/u/6747040?v=4" width="100px;" alt=""/><br /><sub><b>almikhayl</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=almikhayl" title="Code">πŸ’»</a> <a href="#platform-almikhayl" title="Packaging/porting to new platform">πŸ“¦</a></td>
+    <td align="center"><a href="https://github.com/andrew-hardin"><img src="https://avatars0.githubusercontent.com/u/16496326?v=4" width="100px;" alt=""/><br /><sub><b>Andrew Hardin</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=andrew-hardin" title="Code">πŸ’»</a></td>
+  </tr>
+  <tr>
+    <td align="center"><a href="https://github.com/SX91"><img src="https://avatars2.githubusercontent.com/u/754754?v=4" width="100px;" alt=""/><br /><sub><b>Anton</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=SX91" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/helmesjo"><img src="https://avatars0.githubusercontent.com/u/2501070?v=4" width="100px;" alt=""/><br /><sub><b>Fred HelmesjΓΆ</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Ahelmesjo" title="Bug reports">πŸ›</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=helmesjo" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/skannan89"><img src="https://avatars0.githubusercontent.com/u/11918764?v=4" width="100px;" alt=""/><br /><sub><b>Kannan</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Askannan89" title="Bug reports">πŸ›</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=skannan89" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="http://himvis.com"><img src="https://avatars3.githubusercontent.com/u/465279?v=4" width="100px;" alt=""/><br /><sub><b>Khem Raj</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=kraj" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://www.mogigoma.com/"><img src="https://avatars2.githubusercontent.com/u/130862?v=4" width="100px;" alt=""/><br /><sub><b>Mak Kolybabi</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=mogigoma" title="Documentation">πŸ“–</a></td>
+    <td align="center"><a href="http://msoeken.github.io"><img src="https://avatars0.githubusercontent.com/u/1998245?v=4" width="100px;" alt=""/><br /><sub><b>Mathias Soeken</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=msoeken" title="Documentation">πŸ“–</a></td>
+    <td align="center"><a href="https://github.com/nathanhourt"><img src="https://avatars2.githubusercontent.com/u/271977?v=4" width="100px;" alt=""/><br /><sub><b>Nathan Hourt</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Anathanhourt" title="Bug reports">πŸ›</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=nathanhourt" title="Code">πŸ’»</a></td>
+  </tr>
+  <tr>
+    <td align="center"><a href="https://github.com/pleroux0"><img src="https://avatars2.githubusercontent.com/u/39619854?v=4" width="100px;" alt=""/><br /><sub><b>Paul le Roux</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=pleroux0" title="Code">πŸ’»</a> <a href="#platform-pleroux0" title="Packaging/porting to new platform">πŸ“¦</a></td>
+    <td align="center"><a href="https://github.com/chfast"><img src="https://avatars1.githubusercontent.com/u/573380?v=4" width="100px;" alt=""/><br /><sub><b>PaweΕ‚ Bylica</b></sub></a><br /><a href="#platform-chfast" title="Packaging/porting to new platform">πŸ“¦</a></td>
+    <td align="center"><a href="https://github.com/peterazmanov"><img src="https://avatars0.githubusercontent.com/u/15322318?v=4" width="100px;" alt=""/><br /><sub><b>Peter Azmanov</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=peterazmanov" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/delpinux"><img src="https://avatars0.githubusercontent.com/u/35096584?v=4" width="100px;" alt=""/><br /><sub><b>StΓ©phane Del Pino</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=delpinux" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/metopa"><img src="https://avatars2.githubusercontent.com/u/3974178?v=4" width="100px;" alt=""/><br /><sub><b>Viacheslav Kroilov</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=metopa" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="http://cs.odu.edu/~ctsolakis"><img src="https://avatars0.githubusercontent.com/u/6725596?v=4" width="100px;" alt=""/><br /><sub><b>christos</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=ChristosT" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/deining"><img src="https://avatars3.githubusercontent.com/u/18169566?v=4" width="100px;" alt=""/><br /><sub><b>deining</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=deining" title="Documentation">πŸ“–</a></td>
+  </tr>
+  <tr>
+    <td align="center"><a href="https://github.com/elszon"><img src="https://avatars0.githubusercontent.com/u/2971495?v=4" width="100px;" alt=""/><br /><sub><b>elszon</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=elszon" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/ncihnegn"><img src="https://avatars3.githubusercontent.com/u/12021721?v=4" width="100px;" alt=""/><br /><sub><b>ncihnegn</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=ncihnegn" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/nurelin"><img src="https://avatars3.githubusercontent.com/u/5276274?v=4" width="100px;" alt=""/><br /><sub><b>nurelin</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=nurelin" title="Code">πŸ’»</a></td>
+    <td align="center"><a href="https://github.com/ryan4729"><img src="https://avatars3.githubusercontent.com/u/40183301?v=4" width="100px;" alt=""/><br /><sub><b>ryan4729</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=ryan4729" title="Tests">⚠️</a></td>
+    <td align="center"><a href="https://izzys.casa"><img src="https://avatars0.githubusercontent.com/u/63051?v=4" width="100px;" alt=""/><br /><sub><b>Isabella Muerte</b></sub></a><br /><a href="#platform-slurps-mad-rips" title="Packaging/porting to new platform">πŸ“¦</a></td>
+    <td align="center"><a href="https://github.com/KOLANICH"><img src="https://avatars1.githubusercontent.com/u/240344?v=4" width="100px;" alt=""/><br /><sub><b>KOLANICH</b></sub></a><br /><a href="#platform-KOLANICH" title="Packaging/porting to new platform">πŸ“¦</a></td>
+    <td align="center"><a href="https://github.com/jgerityneurala"><img src="https://avatars2.githubusercontent.com/u/57360646?v=4" width="100px;" alt=""/><br /><sub><b>James Gerity</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=jgerityneurala" title="Documentation">πŸ“–</a></td>
+  </tr>
+</table>
+
+<!-- markdownlint-enable -->
+<!-- prettier-ignore-end -->
+<!-- ALL-CONTRIBUTORS-LIST:END -->
+
+
+This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
 
 ## License
 
@@ -864,11 +925,13 @@ CLI11 was developed at the [University of Cincinnati][] to support of the [GooFi
 [doi-badge]: https://zenodo.org/badge/80064252.svg
 [doi-link]: https://zenodo.org/badge/latestdoi/80064252
 [azure-badge]: https://dev.azure.com/CLIUtils/CLI11/_apis/build/status/CLIUtils.CLI11?branchName=master
-[azure]: https://dev.azure.com/CLIUtils/CLI11/_build/|latest?definitionId=1&branchName=master
+[azure]: https://dev.azure.com/CLIUtils/CLI11/_build/latest?definitionId=1&branchName=master
 [travis-badge]: https://img.shields.io/travis/CLIUtils/CLI11/master.svg?label=Linux/macOS
 [travis]: https://travis-ci.org/CLIUtils/CLI11
-[appveyor-badge]: https://img.shields.io/appveyor/ci/HenrySchreiner/cli11/master.svg?label=Windows
+[appveyor-badge]: https://img.shields.io/appveyor/ci/HenrySchreiner/cli11/master.svg?label=AppVeyor
 [appveyor]: https://ci.appveyor.com/project/HenrySchreiner/cli11
+[actions-badge]: https://github.com/CLIUtils/CLI11/workflows/Tests/badge.svg
+[actions-link]:  https://github.com/CLIUtils/CLI11/actions
 [codecov-badge]: https://codecov.io/gh/CLIUtils/CLI11/branch/master/graph/badge.svg
 [codecov]: https://codecov.io/gh/CLIUtils/CLI11
 [gitter-badge]: https://badges.gitter.im/CLI11gitter/Lobby.svg
@@ -895,7 +958,7 @@ CLI11 was developed at the [University of Cincinnati][] to support of the [GooFi
 [nsf award 1414736]: https://nsf.gov/awardsearch/showAward?AWD_ID=1414736
 [university of cincinnati]: http://www.uc.edu
 [gitbook]: https://cliutils.github.io/CLI11/book/
-[cli11 internals]: https://cliutils.gitlab.io/CLI11Tutorial/chapters/internals.html
+[cli11 advanced topics/custom converters]: https://cliutils.gitlab.io/CLI11Tutorial/chapters/advanced-topics.html#custom-converters
 [programoptions.hxx]: https://github.com/Fytch/ProgramOptions.hxx
 [argument aggregator]: https://github.com/vietjtnguyen/argagg
 [args]: https://github.com/Taywee/args
@@ -906,8 +969,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.8-online-blue.svg
-[wandbox-link]: https://wandbox.org/permlink/EbI6nT3RkYKviKV2
+[wandbox-badge]: https://img.shields.io/badge/try_1.9-online-blue.svg
+[wandbox-link]: https://wandbox.org/permlink/8SirASwhbFQZyDTW
 [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
diff --git a/packages/CLI11/azure-pipelines.yml b/packages/CLI11/azure-pipelines.yml
index 180e29735f8a8abbe41e78ba8404993e3044d345..d01a240690edbc7499ca7fa71495e5fde747ed7d 100644
--- a/packages/CLI11/azure-pipelines.yml
+++ b/packages/CLI11/azure-pipelines.yml
@@ -10,7 +10,7 @@ variables:
   cli11.single: ON
   cli11.std: 14
   cli11.build_type: Debug
-  cli11.options:
+  cli11.options: -DCLI11_EXAMPLES_JSON=ON
   CMAKE_BUILD_PARALLEL_LEVEL: 4
 
 jobs:
@@ -18,28 +18,45 @@ jobs:
 - job: ClangTidy
   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.options: -DCLI11_CLANG_TIDY=ON -DCLI11_CLANG_TIDY_OPTIONS="-fix"
     cli11.std: 11
     cli11.single: OFF
     CMAKE_BUILD_PARALLEL_LEVEL: 1
   pool:
-    vmImage: 'ubuntu-16.04'
-  container: silkeh/clang:5
+    vmImage: 'ubuntu-latest'
+  container: silkeh/clang:8
   steps:
     - template: .ci/azure-cmake.yml
     - template: .ci/azure-build.yml
     - script: git diff --exit-code --color
       displayName: Check tidy
 
+- job: CppLint
+  pool:
+    vmImage: 'ubuntu-latest'
+  container: sharaku/cpplint:latest
+  steps:
+    - bash: cpplint --counting=detailed --recursive examples include/CLI
+      displayName: Checking against google style guide
+
+# TODO: Fix macOS error and windows warning in c++17 mode
 - job: Native
   strategy:
     matrix:
-      Linux:
+      Linux14:
         vmImage: 'ubuntu-latest'
-      macOS:
+      macOS17:
+        vmImage: 'macOS-latest'
+        cli11.std: 17
+      macOS11:
         vmImage: 'macOS-latest'
-      Windows:
+        cli11.std: 11
+      Windows17:
+        vmImage: 'vs2017-win2016'
+        cli11.std: 17
+      Windows11:
         vmImage: 'vs2017-win2016'
+        cli11.std: 11
   pool:
     vmImage: $(vmImage)
   steps:
@@ -75,6 +92,7 @@ jobs:
       gcc4.8:
         containerImage: gcc:4.8
         cli11.std: 11
+        cli11.options:
       clang3.4:
         containerImage: silkeh/clang:3.4
         cli11.std: 11
@@ -87,4 +105,3 @@ jobs:
     - template: .ci/azure-cmake.yml
     - template: .ci/azure-build.yml
     - template: .ci/azure-test.yml
-
diff --git a/packages/CLI11/book/CMakeLists.txt b/packages/CLI11/book/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e99bde60600fa985350a6c76f2e63b10b64626a9
--- /dev/null
+++ b/packages/CLI11/book/CMakeLists.txt
@@ -0,0 +1,10 @@
+
+set(
+    book_sources
+    README.md
+    SUMMARY.md
+)
+
+file(GLOB book_chapters RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} chapters/*.md)
+add_custom_target(cli_book SOURCES ${book_sources} ${book_chapters})
+
diff --git a/packages/CLI11/book/README.md b/packages/CLI11/book/README.md
index 75020fb10b70197755672d81d2200bdbd68c35f1..8af5a7ecc12c8196796dd8ea4915287a9c2d43d3 100644
--- a/packages/CLI11/book/README.md
+++ b/packages/CLI11/book/README.md
@@ -6,7 +6,7 @@ The library was inspired the Python libraries [Plumbum] and [Click], and incorpo
 
 > 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:
+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 0:
 
 ```term
 gitbook $ ./a.out
@@ -28,7 +28,7 @@ Like any good command line application, help is provided. This program can be im
 
 [include](code/intro.cpp)
 
-[Source code](https://gitlab.com/CLIUtils/CLI11Tutorial/blob/master/code/intro.cpp)
+[Source code](https://github.com/CLIUtils/CLI11/blob/master/book/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:
 
@@ -53,19 +53,17 @@ Reading/producing `.ini` files for configuration is also supported, as is using
 
 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
+[CLI11Tutorial]: https://cliutils.github.io/CLI11/book
 [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/
+[Click]: https://click.palletsprojects.com/
 
 
 
diff --git a/packages/CLI11/book/chapters/an-advanced-example.md b/packages/CLI11/book/chapters/an-advanced-example.md
index 6a9bd265373c652e1737e579371fd9bc64426c72..2b20dde15d37fed0cb05094e7160a8887ef96050 100644
--- a/packages/CLI11/book/chapters/an-advanced-example.md
+++ b/packages/CLI11/book/chapters/an-advanced-example.md
@@ -18,7 +18,7 @@ All that's need now is the parse call. We'll print a little message after the co
 
 [include:"Parse"](../code/geet.cpp)
 
-[Source code](https://gitlab.com/CLIUtils/CLI11Tutorial/blob/master/code/flags.cpp)
+[Source code](https://github.com/CLIUtils/CLI11/tree/master/book/code/geet.cpp)
 
 If you compile and run:
 
diff --git a/packages/CLI11/book/chapters/config.md b/packages/CLI11/book/chapters/config.md
index 85c5b187a86801bc8ba5e6db7ed2bba848c22eb8..cfd570acd0cc4fbff91cde5372b2951ea0e03b16 100644
--- a/packages/CLI11/book/chapters/config.md
+++ b/packages/CLI11/book/chapters/config.md
@@ -2,15 +2,49 @@
 
 ## 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.
+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.
+
+### Extra fields
+Sometimes configuration files are used for multiple purposes so CLI11 allows options on how to deal with extra fields
+
+```cpp
+app.allow_config_extras(true);
+```
+will allow capture the extras in the extras field of the app. (NOTE:  This also sets the `allow_extras` in the app to true)
+
+```cpp
+app.allow_config_extras(false);
+```
+will generate an error if there are any extra fields
+
+for slightly finer control there is a scoped enumeration of the modes
+or
+```cpp
+app.allow_config_extras(CLI::config_extras_mode::ignore);
+```
+will completely ignore extra parameters in the config file.   This mode is the default.
+
+```cpp
+app.allow_config_extras(CLI::config_extras_mode::capture);
+```
+will store the unrecognized options in the app extras fields. This option is the closest equivalent to `app.allow_config_extras(true);` with the exception that it does not also set the `allow_extras` flag so using this option without also setting `allow_extras(true)` will generate an error which may or may not be the desired behavior.
+
+```cpp
+app.allow_config_extras(CLI::config_extras_mode::error);
+```
+is equivalent to `app.allow_config_extras(false);`
+
+### Getting the used configuration file name
+If it is needed to get the configuration file name used this can be obtained via
+`app.get_config_ptr()->as<std::string>()`  or
+`app["--config"]->as<std::string>()` assuming `--config` was the configuration option name.
 
 ## Configure file format
 
 Here is an example configuration file, in INI format:
 
 ```ini
-; Commments are supported, using a ;
+; Comments are supported, using a ;
 ; The default section is [default], case insensitive
 
 value = 1
@@ -23,12 +57,66 @@ 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".
+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`, `y`, `t`, `+`, `yes`, `enable`; or `false`, `off`, `0`, `no`, `n`, `f`, `-`, `disable`, (case insensitive). Sections (and `.` separated names) are treated as subcommands (note: this does not necessarily mean that subcommand was passed, it just sets the "defaults". If a subcommand is set to `configurable` then passing the subcommand using `[sub]` in a configuration file will trigger the subcommand.)
+
+CLI11 also supports configuration file in [TOML](https://github.com/toml-lang/toml) format.
+
+```toml
+# Comments 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
+[subcommand.sub]
+subcommand = true # could also be give as sub.subcommand=true
+```
+
+The main differences are in vector notation and comment character.  Note: CLI11 is not a full TOML parser as it just reads values as strings.  It is possible (but not recommended) to mix notation.
 
 ## 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.
 
+### Customization of configure file output
+The default config parser/generator has some customization points that allow variations on the INI format.  The default formatter has a base configuration that matches the INI format.  It defines 5 characters that define how different aspects of the configuration are handled
+```cpp
+/// the character used for comments
+char commentChar = ';';
+/// the character used to start an array '\0' is a default to not use
+char arrayStart = '\0';
+/// the character used to end an array '\0' is a default to not use
+char arrayEnd = '\0';
+/// the character used to separate elements in an array
+char arraySeparator = ' ';
+/// the character used separate the name from the value
+char valueDelimiter = '=';
+```
+
+These can be modified via setter functions
+
+- ` ConfigBase *comment(char cchar)` Specify the character to start a comment block
+-  `ConfigBase *arrayBounds(char aStart, char aEnd)`  Specify the start and end characters for an array
+-  `ConfigBase *arrayDelimiter(char aSep)` Specify the delimiter character for an array
+-  `ConfigBase *valueSeparator(char vSep)` Specify the delimiter between a name and value
+
+For example to specify reading a configure file that used `:` to separate name and values
+
+```cpp
+auto config_base=app.get_config_formatter_base();
+config_base->valueSeparator(':');
+```
+
+The default configuration file will read TOML files, but will write out files in the INI format.  To specify outputting TOML formatted files use
+```cpp
+app.config_formatter(std::make_shared<CLI::ConfigTOML>());
+```
+which makes use of a predefined modification of the ConfigBase class which INI also uses.
+
 ## Custom formats
 
 {% hint style='info' %}
@@ -51,3 +139,9 @@ 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.
+
+
+## Triggering Subcommands
+Configuration files can be used to trigger subcommands if a subcommand is set to configure.  By default configuration file just set the default values of a subcommand.  But if the `configure()` option is set on a subcommand then the if the subcommand is utilized via a `[subname]` block in the configuration file it will act as if it were called from the command line.  Subsubcommands can be triggered via [subname.subsubname].  Using the `[[subname]]` will be as if the subcommand were triggered multiple times from the command line.  This functionality can allow the configuration file to act as a scripting file.
+
+For custom configuration files this behavior can be triggered by specifying the parent subcommands in the structure and `++` as the name to open a new subcommand scope and `--` to close it.  These names trigger the different callbacks of configurable subcommands.
diff --git a/packages/CLI11/book/chapters/flags.md b/packages/CLI11/book/chapters/flags.md
index abab30672905affc0976b9412aaface5461c5c08..d4f88fb6895a9d5402e14e6c65e2975311f94e53 100644
--- a/packages/CLI11/book/chapters/flags.md
+++ b/packages/CLI11/book/chapters/flags.md
@@ -7,7 +7,7 @@ The most basic addition to a command line program is a flag. This is simply some
 The simplest way to add a flag is probably a boolean flag:
 
 ```cpp
-bool my_flag;
+bool my_flag{false};
 app.add_flag("-f", my_flag, "Optional description");
 ```
 
@@ -19,12 +19,38 @@ This will bind the flag `-f` to the boolean `my_flag`. After the parsing step, `
 If you want to allow multiple flags, simply use any integer-like instead of a bool:
 
 ```cpp
-int my_flag;
+int my_flag{0};
 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.
 
+## Arbitrary type flags
+
+CLI11 allows the type of the variable to assign to in the `add_flag` function to be any supported type.   This is particularly useful in combination with specifying default values for flags.  The allowed types include  bool, int, float, vector, enum, or string-like.
+
+### Default Flag Values
+
+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");
+```
+
+specifies that if `--flag` is passed on the command line result will be true or contain a value of 1. If `--no-flag` is
+passed `result` will contain false or -1 if `result` is a signed integer type, or 0 if it is an unsigned type.  An
+alternative form of the syntax is more explicit: `"--flag,--no-flag{false}"`; this is equivalent to the previous
+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:
+
+```cpp
+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.
+
 ## 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:
@@ -37,7 +63,7 @@ After parsing, you can use `my_flag->count()` to count the number of times this
 
 ## 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.
+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(std::size_t)`. This could be useful for a version printout, etc.
 
 ```
 auto callback = [](int count){std::cout << "This was called " << count << " times";};
@@ -52,7 +78,7 @@ The name string, the first item of every `add_` method, can contain as many shor
 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;
+bool flag{false};
 app.add_flag("--flag", flag)
     ->ignore_case();
 ```
@@ -73,7 +99,7 @@ The values would be used like this:
 
 [include:"usage"](../code/flags.cpp)
 
-[Source code](https://gitlab.com/CLIUtils/CLI11Tutorial/blob/master/code/flags.cpp)
+[Source code](https://github.com/CLIUtils/CLI11/tree/master/book/code/flags.cpp)
 
 If you compile and run:
 
diff --git a/packages/CLI11/book/chapters/installation.md b/packages/CLI11/book/chapters/installation.md
index 22870740080878da165aa73b2da1370e3d40508d..c7bcc18f3a90594705be88b246fe2f532aa6ce49 100644
--- a/packages/CLI11/book/chapters/installation.md
+++ b/packages/CLI11/book/chapters/installation.md
@@ -86,6 +86,7 @@ For the curious, the CMake options and defaults are listed below. Most options d
 | `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+. |
+| `CLI11_CLANG_TIDY=OFF` | Run `clang-tidy` on the examples and headers. Requires CMake 3.6+. |
+| `CLI11_CLANG_TIDY_OPTIONS=""` | Options to pass to `clang-tidy`, such as `-fix` (single threaded build only if applying fixes!) |
 
 [^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/options.md b/packages/CLI11/book/chapters/options.md
index 112959809d1001740015817cab9c64ed6c5944cd..e98cf4a2a08b2071910e347ab59d2a0e05b766fc 100644
--- a/packages/CLI11/book/chapters/options.md
+++ b/packages/CLI11/book/chapters/options.md
@@ -5,14 +5,14 @@ The most versatile addition to a command line program is a option. This is like
 
 
 ```cpp
-int int_option;
+int int_option{0};
 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;
+int int_option{0};
 app.add_option("-i", int_option, "Optional description", true);
 ```
 
@@ -72,23 +72,26 @@ When you call `add_option`, you get a pointer to the added option. You can use t
 
 | 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()`.|
+| `->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, mainly for vector args. |
+| `->expected(Nmin,Nmax)` | Take between `Nmin` and `Nmax` values. |
+| `->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). |
+| `->allow_extra_args()` | Allow extra argument values to be included when an option is passed. Enabled by default for vector options. |
+| `->multi_option_policy(CLI::MultiOptionPolicy::Throw)` | Sets the policy for handling multiple arguments if the option was received on the command line several times. `Throw`ing an error is the default, but `TakeLast`, `TakeFirst`, `TakeAll`, 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*.|
+| `->join()` | sets `->multi_option_policy(CLI::MultiOptionPolicy::Join)`, which uses newlines or the specified delimiter to join all arguments into a single string output. |
+| `->join(delim)` | sets `->multi_option_policy(CLI::MultiOptionPolicy::Join)`, which uses `delim` 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.
 
@@ -101,7 +104,7 @@ CLI::Option* opt = app.add_flag("--opt");
 
 CLI11_PARSE(app, argv, argc);
 
-if(*opt)
+if(* opt)
     std::cout << "Flag recieved " << opt->count() << " times." << std::endl;
 ```
 
@@ -109,21 +112,21 @@ if(*opt)
 
 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
+*   `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.option_defaults()->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.
+Groups are mostly for visual organization, but an empty string for a group name will hide the option.
 
 
 ## Listing of specialty options:
@@ -135,7 +138,7 @@ Besides `add_option` and `add_flag`, there are several special ways to create op
 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;
+int val{0};
 app.add_set("--even", val, {0,2,4,6,8});
 ```
 
@@ -144,40 +147,72 @@ app.add_set("--even", val, {0,2,4,6,8});
 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;
+std::complex<float> val{0.0F,0.0F};
 app.add_complex("--cplx", val);
 ```
 
-### Optionals (New in CLI11 1.5)
+### 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.
 
-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:
+## Parse configuration
 
+How an option and its arguments are parsed depends on a set of controls that are part of the option structure.  In most circumstances these controls are set automatically based on the function used to create the option and the type the arguments are parsed into.  The variables define the size of the underlying type (essentially how many strings make up the type), the expected size (how many groups are expected) and a flag indicating if multiple groups are allowed with a single option.  And these interact with the `multi_option_policy` when it comes time to parse.
+
+### examples
+How options manage this is best illustrated through some examples
 ```cpp
-#define CLI11_BOOST_OPTIONAL
-#include <CLI/CLI.hpp>
+std::string val;
+app.add_option("--opt",val,"description");
+```
+creates an option that assigns a value to a `std::string`  When this option is constructed it sets a type_size of 1.  meaning that the assignment uses a single string.  The Expected size is also set to 1 by default, and `allow_extra_args` is set to false. meaning that each time this option is called 1 argument is expected.  This would also be the case if val were a `double`, `int` or any other single argument types.
+
+now for example
+```cpp
+std::pair<int, std::string> val;
+app.add_option("--opt",val,"description");
+```
 
-...
+In this case the typesize is automatically detected to be 2 instead of 1, so the parsing would expect 2 arguments associated with the option.
 
-boost::optional<int> x;
-app.add_option("-x", x);
+```cpp
+std::vector<int> val;
+app.add_option("--opt",val,"description");
+```
 
-CLI11_PARSE(app, argc, argv);
+detects a type size of 1, since the underlying element type is a single string, so the minimum number of strings is 1.  But since it is a vector the expected number can be very big.  The default for a vector is (1<<30), and the allow_extra_args is set to true.  This means that at least 1 argument is expected to follow the option, but arbitrary numbers of arguments may follow.  These are checked if they have the form of an option but if not they are added to the argument.
 
-if(x)
-    std::cout << *x << std::endl;
+```cpp
+std::vector<std::tuple<int, double, std::string>> val;
+app.add_option("--opt",val,"description");
 ```
+gets into the complicated cases where the type size is now 3.  and the expected max is set to a large number and `allow_extra_args` is set to true.  In this case at least 3 arguments are required to follow the option,  and subsequent groups must come in groups of three, otherwise an error will result.
 
-### Windows style options (New in CLI11 1.7)
+```cpp
+bool val{false};
+app.add_flag("--opt",val,"description");
+```
 
-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:
+Using the add_flag methods for creating options creates an option with an expected size of 0, implying no arguments can be passed.
 
-* `/a` (flag)
-* `/f filename` (option)
-* `/long` (long flag)
-* `/file filename` (space)
-* `/file:filename` (colon)
+### Customization
 
-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.
+The `type_size(N)`, `type_size(Nmin, Nmax)`, `expected(N)`, `expected(Nmin,Nmax)`, and `allow_extra_args()` can be used to customize an option.  For example
+
+```cpp
+std::string val;
+auto opt=app.add_flag("--opt{vvv}",val,"description");
+opt->expected(0,1);
+```
+will create a hybrid option, that can exist on its own in which case the value "vvv" is used or if a value is given that value will be used.
 
 [^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
index ea85bed0650c2a8ca9e794312686b90ef5d5715a..ee2b551081c1cb9f0a758be45d2e237e32028951 100644
--- a/packages/CLI11/book/chapters/subcommands.md
+++ b/packages/CLI11/book/chapters/subcommands.md
@@ -106,7 +106,7 @@ allows that option, it matches there instead. This was added to allow CLI11 to r
 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.
+Here, `--shared_flag` 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
 
diff --git a/packages/CLI11/cmake/AddGoogletest.cmake b/packages/CLI11/cmake/AddGoogletest.cmake
index cd7031a57c0d565daa5ff2bc357ee715dc37cb81..ae0dc18fcb96f974148645e2ab2fb0abc8efe564 100644
--- a/packages/CLI11/cmake/AddGoogletest.cmake
+++ b/packages/CLI11/cmake/AddGoogletest.cmake
@@ -37,6 +37,10 @@ macro(add_gtest TESTNAME)
     else()
         add_test(${TESTNAME} ${TESTNAME})
         set_target_properties(${TESTNAME} PROPERTIES FOLDER "Tests")
+        if (CLI11_FORCE_LIBCXX)
+           set_property(TARGET ${T} APPEND_STRING
+             PROPERTY LINK_FLAGS -stdlib=libc++)
+         endif()
     endif()
 
 endmacro()
diff --git a/packages/CLI11/cmake/CodeCoverage.cmake b/packages/CLI11/cmake/CodeCoverage.cmake
index 5c3d41c9960ce92d7aaaeae50e5cc11ac94c8b97..907cf72d01094f84568dd0e214ab8942e980c24a 100644
--- a/packages/CLI11/cmake/CodeCoverage.cmake
+++ b/packages/CLI11/cmake/CodeCoverage.cmake
@@ -87,7 +87,7 @@ elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
     message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
 endif()
 
-set(COVERAGE_COMPILER_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage"
+set(COVERAGE_COMPILER_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage -fno-inline -fno-inline-small-functions -fno-default-inline"
     CACHE INTERNAL "")
 
 set(CMAKE_CXX_FLAGS_COVERAGE
diff --git a/packages/CLI11/conanfile.py b/packages/CLI11/conanfile.py
index 54c1d5adfdc5b12d431bf81c0f2f5599c6423dfe..6ce8a01e3ddabd2db86e63d554e82a1b208eb7aa 100644
--- a/packages/CLI11/conanfile.py
+++ b/packages/CLI11/conanfile.py
@@ -30,6 +30,7 @@ class CLI11Conan(ConanFile):
         "extern/*",
         "cmake/*",
         "CMakeLists.txt",
+        "CLI11.CPack.Description.txt",
         "tests/*",
     )
 
diff --git a/packages/CLI11/examples/CMakeLists.txt b/packages/CLI11/examples/CMakeLists.txt
index 07ed55a4a6cb5e3ebbd369dcb71e0711f8d10dad..2359d5cd8d64ddf5a8a61524624e73f2403b93af 100644
--- a/packages/CLI11/examples/CMakeLists.txt
+++ b/packages/CLI11/examples/CMakeLists.txt
@@ -1,46 +1,50 @@
+
 function(add_cli_exe T)
     add_executable(${T} ${ARGN} ${CLI11_headers})
     target_link_libraries(${T} PUBLIC CLI11)
-    set_target_properties(
-         ${T} PROPERTIES
-         FOLDER "Examples"
-         )
-
-    if(CLANG_TIDY_EXE)
-    set_target_properties(
-        ${T} PROPERTIES
-        CXX_CLANG_TIDY "${DO_CLANG_TIDY}"
-        )
+    set_property(TARGET ${T} PROPERTY FOLDER "Examples")
+    if(CLI11_FORCE_LIBCXX)
+        set_property(TARGET ${T} APPEND_STRING PROPERTY LINK_FLAGS -stdlib=libc++)
+    endif()
+    if(CLI11_CLANG_TIDY)
+        set_property(TARGET ${T} PROPERTY CXX_CLANG_TIDY "${DO_CLANG_TIDY}")
     endif()
 endfunction()
 
-option(CLI11_EXAMPLE_JSON OFF)
-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.8)
-        message(WARNING "The json example requires GCC 4.8+ (requirement on json library)")
+if(CLI11_BUILD_EXAMPLES_JSON)
+    message(STATUS "Using nlohmann/json")
+    FetchContent_Declare(json
+        URL https://github.com/nlohmann/json/releases/download/v3.7.3/include.zip
+        URL_HASH "SHA256=87b5884741427220d3a33df1363ae0e8b898099fbc59f1c451113f6732891014"
+    )
+
+    FetchContent_GetProperties(json)
+    if (NOT json_POPULATED)
+        FetchContent_Populate(json)
     endif()
+
     add_cli_exe(json json.cpp)
-    target_include_directories(json PUBLIC SYSTEM "${CLI11_SOURCE_DIR}/extern/json/single_include")
+    target_include_directories(json PUBLIC SYSTEM "${json_SOURCE_DIR}/single_include")
 
     add_test(NAME json_config_out COMMAND json --item 2)
     set_property(TEST json_config_out PROPERTY PASS_REGULAR_EXPRESSION
-        "{"
-        "\"item\": \"2\""
-        "\"simple\": false"
-        "}")
+        [[{]]
+        [["item": "2"]]
+        [["simple": false]]
+        [[}]]
+        )
 
     file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/input.json" [=[{"item":3,"simple":false}]=])
     add_test(NAME json_config_in COMMAND json --config "${CMAKE_CURRENT_BINARY_DIR}/input.json")
     set_property(TEST json_config_in PROPERTY PASS_REGULAR_EXPRESSION
-        "{"
-        "\"item\": \"3\""
-        "\"simple\": false"
-        "}")
+        [[{]]
+        [["item": "3"]]
+        [["simple": false]]
+        [[}]]
+)
 endif()
 
+
 add_cli_exe(simple simple.cpp)
 add_test(NAME simple_basic COMMAND simple)
 add_test(NAME simple_all COMMAND simple -f filename.txt -c 12 --flag --flag -d 1.2)
@@ -203,6 +207,11 @@ add_test(NAME enum_fail COMMAND enum -l 4)
 set_property(TEST enum_fail PROPERTY PASS_REGULAR_EXPRESSION
     "--level: Check 4 value in {" "FAILED")
 
+add_cli_exe(enum_ostream enum_ostream.cpp)
+add_test(NAME enum_ostream_pass COMMAND enum_ostream --level medium)
+set_property(TEST enum_ostream_pass PROPERTY PASS_REGULAR_EXPRESSION
+    "Enum received: Medium")
+
 add_cli_exe(digit_args digit_args.cpp)
 add_test(NAME digit_args COMMAND digit_args -h)
 set_property(TEST digit_args PROPERTY PASS_REGULAR_EXPRESSION
@@ -226,3 +235,21 @@ add_cli_exe(nested nested.cpp)
 add_cli_exe(subcom_help subcom_help.cpp)
 add_test(NAME subcom_help_normal COMMAND subcom_help sub --help)
 add_test(NAME subcom_help_reversed COMMAND subcom_help --help sub)
+
+add_cli_exe(retired retired.cpp)
+add_test(NAME retired_retired_test COMMAND retired --retired_option)
+add_test(NAME retired_retired_test2 COMMAND retired --retired_option 567)
+add_test(NAME retired_retired_test3 COMMAND retired --retired_option2 567 689 789)
+add_test(NAME retired_deprecated COMMAND retired --deprecate 19 20)
+
+set_property(TEST retired_retired_test PROPERTY PASS_REGULAR_EXPRESSION
+    "WARNING.*retired")
+
+set_property(TEST retired_retired_test2 PROPERTY PASS_REGULAR_EXPRESSION
+    "WARNING.*retired")
+
+set_property(TEST retired_retired_test3 PROPERTY PASS_REGULAR_EXPRESSION
+    "WARNING.*retired")
+
+set_property(TEST retired_deprecated PROPERTY PASS_REGULAR_EXPRESSION
+    "deprecated.*not_deprecated")
diff --git a/packages/CLI11/examples/callback_passthrough.cpp b/packages/CLI11/examples/callback_passthrough.cpp
index 8d0203b1f16fe0cbd4624c2cf6fbdb822ef89d4d..16698aafb9a3048e2b124d8726a0604a225a44f6 100644
--- a/packages/CLI11/examples/callback_passthrough.cpp
+++ b/packages/CLI11/examples/callback_passthrough.cpp
@@ -1,4 +1,12 @@
-#include "CLI/CLI.hpp"
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <string>
 
 int main(int argc, char **argv) {
 
diff --git a/packages/CLI11/examples/digit_args.cpp b/packages/CLI11/examples/digit_args.cpp
index c2193c8754d8f9e392fcd5d4e1d6134aa3cb6f67..cf8f7d3317da7864481b031e4ff7fff37744e07f 100644
--- a/packages/CLI11/examples/digit_args.cpp
+++ b/packages/CLI11/examples/digit_args.cpp
@@ -1,10 +1,16 @@
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
 #include <CLI/CLI.hpp>
 #include <iostream>
 
 int main(int argc, char **argv) {
     CLI::App app;
 
-    int val;
+    int val{0};
     // add a set of flags with default values associate with them
     app.add_flag("-1{1},-2{2},-3{3},-4{4},-5{5},-6{6}, -7{7}, -8{8}, -9{9}", val, "compression level");
 
diff --git a/packages/CLI11/examples/enum.cpp b/packages/CLI11/examples/enum.cpp
index 6e0d69b271b1b7765723a845374670c510308baa..a73e56b3f847b719f8f4ddfb979c849e29b82468 100644
--- a/packages/CLI11/examples/enum.cpp
+++ b/packages/CLI11/examples/enum.cpp
@@ -1,15 +1,23 @@
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
 #include <CLI/CLI.hpp>
+#include <iostream>
+#include <map>
+#include <string>
 
 enum class Level : int { High, Medium, Low };
 
 int main(int argc, char **argv) {
     CLI::App app;
 
-    Level level;
+    Level level{Level::Low};
     // specify string->value mappings
-    std::vector<std::pair<std::string, Level>> map{
-        {"high", Level::High}, {"medium", Level::Medium}, {"low", Level::Low}};
-    // checked Transform does the translation and checks the results are either in one of the strings or one of the
+    std::map<std::string, Level> map{{"high", Level::High}, {"medium", Level::Medium}, {"low", Level::Low}};
+    // CheckedTransformer translates and checks whether the results are either in one of the strings or in one of the
     // translations already
     app.add_option("-l,--level", level, "Level settings")
         ->required()
@@ -18,7 +26,7 @@ int main(int argc, char **argv) {
     CLI11_PARSE(app, argc, argv);
 
     // CLI11's built in enum streaming can be used outside CLI11 like this:
-    using namespace CLI::enums;
+    using CLI::enums::operator<<;
     std::cout << "Enum received: " << level << std::endl;
 
     return 0;
diff --git a/packages/CLI11/examples/enum_ostream.cpp b/packages/CLI11/examples/enum_ostream.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..13d33502aea4a994fc2d08b2ab79f0192d296ad0
--- /dev/null
+++ b/packages/CLI11/examples/enum_ostream.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <map>
+#include <string>
+
+enum class Level : int { High, Medium, Low };
+
+// Defining operator<<() for your enum class (in this case for 'Level') overrides CLI11's enum streaming
+inline std::ostream &operator<<(std::ostream &os, const Level &level) {
+    switch(level) {
+    case Level::High:
+        os << "High";
+        break;
+    case Level::Medium:
+        os << "Medium";
+        break;
+    case Level::Low:
+        os << "Low";
+        break;
+    }
+    os << " (ft rom custom ostream)";
+    return os;
+}
+
+int main(int argc, char **argv) {
+    CLI::App app;
+
+    Level level{Level::Low};
+    // specify string->value mappings
+    std::map<std::string, Level> map{{"high", Level::High}, {"medium", Level::Medium}, {"low", Level::Low}};
+    // CheckedTransformer translates and checks whether the results are either in one of the strings or in one of the
+    // translations already
+    app.add_option("-l,--level", level, "Level settings")
+        ->required()
+        ->transform(CLI::CheckedTransformer(map, CLI::ignore_case));
+
+    CLI11_PARSE(app, argc, argv);
+
+    // CLI11's built in enum streaming can be used outside CLI11 like this:
+    using CLI::enums::operator<<;
+    std::cout << "Enum received: " << level << std::endl;
+
+    return 0;
+}
diff --git a/packages/CLI11/examples/formatter.cpp b/packages/CLI11/examples/formatter.cpp
index 4d67ec6126620435a023b7a94d4aed24cf584ab5..81d6209f4866def06a5e300fae3dd1051791bd46 100644
--- a/packages/CLI11/examples/formatter.cpp
+++ b/packages/CLI11/examples/formatter.cpp
@@ -1,4 +1,12 @@
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
 #include <CLI/CLI.hpp>
+#include <iostream>
+#include <memory>
 
 class MyFormatter : public CLI::Formatter {
   public:
diff --git a/packages/CLI11/examples/groups.cpp b/packages/CLI11/examples/groups.cpp
index f8de448fd926f45895af01e19e4d42118c38313f..863976b4ab2b48b5cfc35ddde9b9fa4a0ffed4a6 100644
--- a/packages/CLI11/examples/groups.cpp
+++ b/packages/CLI11/examples/groups.cpp
@@ -1,5 +1,13 @@
-#include "CLI/CLI.hpp"
-#include "CLI/Timer.hpp"
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <CLI/Timer.hpp>
+#include <iostream>
+#include <string>
 
 int main(int argc, char **argv) {
     CLI::AutoTimer("This is a timer");
@@ -9,10 +17,10 @@ int main(int argc, char **argv) {
     std::string file;
     CLI::Option *opt = app.add_option("-f,--file,file", file, "File name")->required()->group("Important");
 
-    int count;
+    int count{0};
     CLI::Option *copt = app.add_flag("-c,--count", count, "Counter")->required()->group("Important");
 
-    double value; // = 3.14;
+    double value{0.0}; // = 3.14;
     app.add_option("-d,--double", value, "Some Value")->group("Other");
 
     try {
diff --git a/packages/CLI11/examples/inter_argument_order.cpp b/packages/CLI11/examples/inter_argument_order.cpp
index 23f8fec3681dbbf5bd2acef13aca3a025e8b0c6a..8655f5994189eb0447884d5e1f4e846135a0c910 100644
--- a/packages/CLI11/examples/inter_argument_order.cpp
+++ b/packages/CLI11/examples/inter_argument_order.cpp
@@ -1,3 +1,9 @@
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
 #include <CLI/CLI.hpp>
 #include <algorithm>
 #include <iostream>
diff --git a/packages/CLI11/examples/json.cpp b/packages/CLI11/examples/json.cpp
index b4c8101ae528659152c10bd9d77ab546c49b3457..d5fe35a700da611a12e1f8bccd77d0c7ded229f5 100644
--- a/packages/CLI11/examples/json.cpp
+++ b/packages/CLI11/examples/json.cpp
@@ -1,5 +1,15 @@
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
 #include <CLI/CLI.hpp>
+#include <iostream>
+#include <memory>
 #include <nlohmann/json.hpp>
+#include <string>
+#include <vector>
 
 // This example is only built on GCC 7 on Travis due to mismatch in stdlib
 // for clang (CLI11 is forgiving about mismatches, json.hpp is not)
diff --git a/packages/CLI11/examples/modhelp.cpp b/packages/CLI11/examples/modhelp.cpp
index a2568322a93e66ef07396c6667472970002075a8..706abcae0c3f319c8218a98f6592ba0f90b7f85c 100644
--- a/packages/CLI11/examples/modhelp.cpp
+++ b/packages/CLI11/examples/modhelp.cpp
@@ -1,7 +1,12 @@
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-#include "CLI/CLI.hpp"
-
+#include <CLI/CLI.hpp>
 #include <iostream>
+#include <string>
 
 int main(int argc, char **argv) {
     CLI::App test{R"raw(Modify the help print so that argument values are accessible.
diff --git a/packages/CLI11/examples/nested.cpp b/packages/CLI11/examples/nested.cpp
index 1714b8df56bf4018d2d409bec791467faf3f772c..7cd49367e3e8d6295f264e0b9fbfdfd96addcc84 100644
--- a/packages/CLI11/examples/nested.cpp
+++ b/packages/CLI11/examples/nested.cpp
@@ -1,3 +1,9 @@
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
 #include <CLI/CLI.hpp>
 #include <string>
 
diff --git a/packages/CLI11/examples/option_groups.cpp b/packages/CLI11/examples/option_groups.cpp
index e3cf301392c782360fc7a1af0a3522fe35832dbc..679cb754b83404c02c325a8073b3b7411f2ef210 100644
--- a/packages/CLI11/examples/option_groups.cpp
+++ b/packages/CLI11/examples/option_groups.cpp
@@ -1,4 +1,12 @@
-#include "CLI/CLI.hpp"
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <string>
 
 int main(int argc, char **argv) {
 
@@ -7,9 +15,9 @@ int main(int argc, char **argv) {
 
     auto format = app.add_option_group("output_format", "formatting type for output");
     auto target = app.add_option_group("output target", "target location for the output");
-    bool csv = false;
-    bool human = false;
-    bool binary = false;
+    bool csv{false};
+    bool human{false};
+    bool binary{false};
     format->add_flag("--csv", csv, "specify the output in csv format");
     format->add_flag("--human", human, "specify the output in human readable text format");
     format->add_flag("--binary", binary, "specify the output in binary format");
diff --git a/packages/CLI11/examples/positional_arity.cpp b/packages/CLI11/examples/positional_arity.cpp
index 0b390df253255fe885be0288ba1d40456cd0315e..eb6639b04856fe90e342254dccc2cb0fd0194076 100644
--- a/packages/CLI11/examples/positional_arity.cpp
+++ b/packages/CLI11/examples/positional_arity.cpp
@@ -1,4 +1,12 @@
-#include "CLI/CLI.hpp"
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <string>
 
 int main(int argc, char **argv) {
 
@@ -6,14 +14,14 @@ int main(int argc, char **argv) {
 
     auto numbers = app.add_option_group("numbers", "specify key numbers");
     auto files = app.add_option_group("files", "specify files");
-    int num1 = -1, num2 = -1;
+    int num1{-1}, num2{-1};
     numbers->add_option("num1", num1, "first number");
     numbers->add_option("num2", num2, "second number");
     std::string file1, file2;
     files->add_option("file1", file1, "first file")->required();
     files->add_option("file2", file2, "second file");
     // set a pre parse callback that turns the numbers group on or off depending on the number of arguments
-    app.preparse_callback([numbers](size_t arity) {
+    app.preparse_callback([numbers](std::size_t arity) {
         if(arity <= 2) {
             numbers->disabled();
         } else {
diff --git a/packages/CLI11/examples/positional_validation.cpp b/packages/CLI11/examples/positional_validation.cpp
index 63ff498c1871b48cf5988c343bda58b9c1178ad2..421de25096cc0273cf928a9a15295b9ecd0db33d 100644
--- a/packages/CLI11/examples/positional_validation.cpp
+++ b/packages/CLI11/examples/positional_validation.cpp
@@ -1,10 +1,18 @@
-#include "CLI/CLI.hpp"
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <string>
 
 int main(int argc, char **argv) {
 
     CLI::App app("test for positional validation");
 
-    int num1 = -1, num2 = -1;
+    int num1{-1}, num2{-1};
     app.add_option("num1", num1, "first number")->check(CLI::Number);
     app.add_option("num2", num2, "second number")->check(CLI::Number);
     std::string file1, file2;
diff --git a/packages/CLI11/examples/prefix_command.cpp b/packages/CLI11/examples/prefix_command.cpp
index edd7a0d6852a643b4cd34262e1abfc9c60a83e00..8977adcbdf95459b89be82fbdef0b7b38628164c 100644
--- a/packages/CLI11/examples/prefix_command.cpp
+++ b/packages/CLI11/examples/prefix_command.cpp
@@ -1,4 +1,13 @@
-#include "CLI/CLI.hpp"
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <string>
+#include <vector>
 
 int main(int argc, char **argv) {
 
diff --git a/packages/CLI11/examples/ranges.cpp b/packages/CLI11/examples/ranges.cpp
index ace778fb86b6505332a90303aa0b907a18f4741a..085a7ad1bec99d898106efef4595f75d3b2a85ef 100644
--- a/packages/CLI11/examples/ranges.cpp
+++ b/packages/CLI11/examples/ranges.cpp
@@ -1,4 +1,12 @@
-#include "CLI/CLI.hpp"
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <vector>
 
 int main(int argc, char **argv) {
 
@@ -8,7 +16,7 @@ int main(int argc, char **argv) {
     app.add_option("--range,-R", range, "A range")->expected(-2);
 
     auto ogroup = app.add_option_group("min_max_step", "set the min max and step");
-    int min, max, step = 1;
+    int min{0}, max{0}, step{1};
     ogroup->add_option("--min,-m", min, "The minimum")->required();
     ogroup->add_option("--max,-M", max, "The maximum")->required();
     ogroup->add_option("--step,-s", step, "The step", true);
diff --git a/packages/CLI11/examples/retired.cpp b/packages/CLI11/examples/retired.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d658e754f5474ef08f23153bb759c602003832d4
--- /dev/null
+++ b/packages/CLI11/examples/retired.cpp
@@ -0,0 +1,46 @@
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <utility>
+#include <vector>
+
+// This example shows the usage of the retired and deprecated option helper methods
+int main(int argc, char **argv) {
+
+    CLI::App app("example for retired/deprecated options");
+    std::vector<int> x;
+    auto opt1 = app.add_option("--retired_option2", x);
+
+    std::pair<int, int> y;
+    auto opt2 = app.add_option("--deprecate", y);
+
+    app.add_option("--not_deprecated", x);
+
+    // specify that a non-existing option is retired
+    CLI::retire_option(app, "--retired_option");
+
+    // specify that an existing option is retired and non-functional: this will replace the option with another that
+    // behaves the same but does nothing
+    CLI::retire_option(app, opt1);
+
+    // deprecate an existing option and specify the recommended replacement
+    CLI::deprecate_option(opt2, "--not_deprecated");
+
+    CLI11_PARSE(app, argc, argv);
+
+    if(!x.empty()) {
+        std::cout << "Retired option example: got --not_deprecated values:";
+        for(auto &xval : x) {
+            std::cout << xval << " ";
+        }
+        std::cout << '\n';
+    } else if(app.count_all() == 1) {
+        std::cout << "Retired option example: no arguments received\n";
+    }
+    return 0;
+}
diff --git a/packages/CLI11/examples/shapes.cpp b/packages/CLI11/examples/shapes.cpp
index 1fcf769613913c076173231f83a908a8cafbb660..880441b85dc729657392a2e6efe485218cd9c9b9 100644
--- a/packages/CLI11/examples/shapes.cpp
+++ b/packages/CLI11/examples/shapes.cpp
@@ -1,4 +1,12 @@
-#include "CLI/CLI.hpp"
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <vector>
 
 int main(int argc, char **argv) {
 
@@ -7,7 +15,7 @@ int main(int argc, char **argv) {
     app.set_help_all_flag("--help-all");
     auto circle = app.add_subcommand("circle", "draw a circle")->immediate_callback();
     double radius{0.0};
-    int circle_counter = 0;
+    int circle_counter{0};
     circle->callback([&radius, &circle_counter] {
         ++circle_counter;
         std::cout << "circle" << circle_counter << " with radius " << radius << std::endl;
@@ -18,7 +26,7 @@ int main(int argc, char **argv) {
     auto rect = app.add_subcommand("rectangle", "draw a rectangle")->immediate_callback();
     double edge1{0.0};
     double edge2{0.0};
-    int rect_counter = 0;
+    int rect_counter{0};
     rect->callback([&edge1, &edge2, &rect_counter] {
         ++rect_counter;
         if(edge2 == 0) {
diff --git a/packages/CLI11/examples/simple.cpp b/packages/CLI11/examples/simple.cpp
index d1ae4716b8cdee0de8b1d2993a05a21af2e28b2e..58cf4b6c7e9c3754ce29fb673ac56895071af26a 100644
--- a/packages/CLI11/examples/simple.cpp
+++ b/packages/CLI11/examples/simple.cpp
@@ -1,4 +1,12 @@
-#include "CLI/CLI.hpp"
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <string>
 
 int main(int argc, char **argv) {
 
@@ -7,13 +15,13 @@ int main(int argc, char **argv) {
     std::string file;
     CLI::Option *opt = app.add_option("-f,--file,file", file, "File name");
 
-    int count;
+    int count{0};
     CLI::Option *copt = app.add_option("-c,--count", count, "Counter");
 
-    int v;
+    int v{0};
     CLI::Option *flag = app.add_flag("--flag", v, "Some flag that can be passed multiple times");
 
-    double value; // = 3.14;
+    double value{0.0}; // = 3.14;
     app.add_option("-d,--double", value, "Some Value");
 
     CLI11_PARSE(app, argc, argv);
diff --git a/packages/CLI11/examples/subcom_help.cpp b/packages/CLI11/examples/subcom_help.cpp
index 0d8997d9d4f11eed94e0793de9ee23e1ee54d28d..d41d4a59b93a798f995f9b39a21788861a57981c 100644
--- a/packages/CLI11/examples/subcom_help.cpp
+++ b/packages/CLI11/examples/subcom_help.cpp
@@ -1,5 +1,12 @@
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
 #include <CLI/CLI.hpp>
 #include <iostream>
+#include <string>
 
 int main(int argc, char *argv[]) {
     CLI::App cli_global{"Demo app"};
diff --git a/packages/CLI11/examples/subcom_in_files/subcommand_a.cpp b/packages/CLI11/examples/subcom_in_files/subcommand_a.cpp
index affc6e849e3abb0bfb14d80c074b6a2806f3b5df..63474fdc3f87bf74e482f80000befe02a87f2af7 100644
--- a/packages/CLI11/examples/subcom_in_files/subcommand_a.cpp
+++ b/packages/CLI11/examples/subcom_in_files/subcommand_a.cpp
@@ -1,8 +1,12 @@
-// ===================================================================
-//   subcommand_a.cpp
-// ===================================================================
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #include "subcommand_a.hpp"
+#include <iostream>
+#include <memory>
 
 /// Set up a subcommand and capture a shared_ptr to a struct that holds all its options.
 /// The variables of the struct are bound to the CLI options.
diff --git a/packages/CLI11/examples/subcom_in_files/subcommand_a.hpp b/packages/CLI11/examples/subcom_in_files/subcommand_a.hpp
index 90625650a9c1f48c274f7631b2701dfd64ac16d2..2993739ea8764dbaf2bb16650fd847e2f719609b 100644
--- a/packages/CLI11/examples/subcom_in_files/subcommand_a.hpp
+++ b/packages/CLI11/examples/subcom_in_files/subcommand_a.hpp
@@ -1,9 +1,12 @@
-// ===================================================================
-//   subcommand_a.hpp
-// ===================================================================
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-#include "CLI/CLI.hpp"
-#include <memory>
+#pragma once
+
+#include <CLI/CLI.hpp>
 #include <string>
 
 /// Collection of all options of Subcommand A.
diff --git a/packages/CLI11/examples/subcom_in_files/subcommand_main.cpp b/packages/CLI11/examples/subcom_in_files/subcommand_main.cpp
index 3befa7a9ad4502f52b38e94837a1d9a5a145f2c7..76ebe7c01e2ca0410d7bf178de8c467106b01b8f 100644
--- a/packages/CLI11/examples/subcom_in_files/subcommand_main.cpp
+++ b/packages/CLI11/examples/subcom_in_files/subcommand_main.cpp
@@ -1,8 +1,11 @@
-// ===================================================================
-//   main.cpp
-// ===================================================================
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
 #include "subcommand_a.hpp"
+#include <CLI/CLI.hpp>
 
 int main(int argc, char **argv) {
     CLI::App app{"..."};
diff --git a/packages/CLI11/examples/subcom_partitioned.cpp b/packages/CLI11/examples/subcom_partitioned.cpp
index d3afdc59839e402c66d3e949f804d7258d291f3b..5010678177b3f1a52e90eca395dfd0d4ef5b4839 100644
--- a/packages/CLI11/examples/subcom_partitioned.cpp
+++ b/packages/CLI11/examples/subcom_partitioned.cpp
@@ -1,5 +1,14 @@
-#include "CLI/CLI.hpp"
-#include "CLI/Timer.hpp"
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <CLI/Timer.hpp>
+#include <iostream>
+#include <memory>
+#include <string>
 
 int main(int argc, char **argv) {
     CLI::AutoTimer("This is a timer");
@@ -10,11 +19,11 @@ int main(int argc, char **argv) {
     std::string file;
     CLI::Option *opt = impOpt->add_option("-f,--file,file", file, "File name")->required();
 
-    int count;
+    int count{0};
     CLI::Option *copt = impOpt->add_flag("-c,--count", count, "Counter")->required();
 
     CLI::App_p otherOpt = std::make_shared<CLI::App>("Other");
-    double value; // = 3.14;
+    double value{0.0}; // = 3.14;
     otherOpt->add_option("-d,--double", value, "Some Value");
 
     // add the subapps to the main one
diff --git a/packages/CLI11/examples/subcommands.cpp b/packages/CLI11/examples/subcommands.cpp
index 580f4d1ace93f3584d690677c7eae590aeba0653..ce71eae94f02cbd2e0c96b138fa29e54fd1099af 100644
--- a/packages/CLI11/examples/subcommands.cpp
+++ b/packages/CLI11/examples/subcommands.cpp
@@ -1,4 +1,12 @@
-#include "CLI/CLI.hpp"
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <string>
 
 int main(int argc, char **argv) {
 
diff --git a/packages/CLI11/examples/validators.cpp b/packages/CLI11/examples/validators.cpp
index d431ed223b87eb7cbf082943e8400afe76733a24..05ea045c0a54e11e47c4b15e9ac94337bb1e479a 100644
--- a/packages/CLI11/examples/validators.cpp
+++ b/packages/CLI11/examples/validators.cpp
@@ -1,4 +1,12 @@
-#include "CLI/CLI.hpp"
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <string>
 
 int main(int argc, char **argv) {
 
@@ -7,7 +15,7 @@ int main(int argc, char **argv) {
     std::string file;
     app.add_option("-f,--file,file", file, "File name")->check(CLI::ExistingFile);
 
-    int count;
+    int count{0};
     app.add_option("-v,--value", count, "Value in range")->check(CLI::Range(3, 6));
     CLI11_PARSE(app, argc, argv);
 
diff --git a/packages/CLI11/extern/json b/packages/CLI11/extern/json
deleted file mode 160000
index 1126c9ca74fdea22d2ce3a065ac0fcb5792cbdaf..0000000000000000000000000000000000000000
--- a/packages/CLI11/extern/json
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 1126c9ca74fdea22d2ce3a065ac0fcb5792cbdaf
diff --git a/packages/CLI11/extern/sanitizers b/packages/CLI11/extern/sanitizers
deleted file mode 160000
index 99e159ec9bc8dd362b08d18436bd40ff0648417b..0000000000000000000000000000000000000000
--- a/packages/CLI11/extern/sanitizers
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 99e159ec9bc8dd362b08d18436bd40ff0648417b
diff --git a/packages/CLI11/include/CLI/App.hpp b/packages/CLI11/include/CLI/App.hpp
index 1c238c26efc90254d6cd7781ab1b693a87028c40..5fa93f06b89f1fba983cd3e10c4a3cbc5f66ea14 100644
--- a/packages/CLI11/include/CLI/App.hpp
+++ b/packages/CLI11/include/CLI/App.hpp
@@ -1,14 +1,19 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
 #include <algorithm>
+#include <cstdint>
 #include <functional>
 #include <iostream>
 #include <iterator>
 #include <memory>
 #include <numeric>
+#include <set>
 #include <sstream>
 #include <string>
 #include <utility>
@@ -45,6 +50,10 @@ std::string simple(const App *app, const Error &e);
 std::string help(const App *app, const Error &e);
 } // namespace FailureMessage
 
+/// enumeration of modes of how to deal with extras in config files
+
+enum class config_extras_mode : char { error = 0, ignore, capture };
+
 class App;
 
 using App_p = std::shared_ptr<App>;
@@ -65,16 +74,17 @@ class App {
     ///@{
 
     /// Subcommand name or program name (from parser if name is empty)
-    std::string name_;
+    std::string name_{};
 
     /// Description of the current program/subcommand
-    std::string description_;
+    std::string description_{};
 
     /// If true, allow extra arguments (ie, don't throw an error). INHERITABLE
     bool allow_extras_{false};
 
-    /// If true, allow extra arguments in the ini file (ie, don't throw an error). INHERITABLE
-    bool allow_config_extras_{false};
+    /// If ignore, allow extra arguments in the ini file (ie, don't throw an error). INHERITABLE
+    /// if error error on an extra argument, and if capture feed it to the app
+    config_extras_mode allow_config_extras_{config_extras_mode::ignore};
 
     ///  If true, return immediately on an unrecognized option (implies allow_extras) INHERITABLE
     bool prefix_command_{false};
@@ -96,32 +106,32 @@ class App {
     bool immediate_callback_{false};
 
     /// This is a function that runs prior to the start of parsing
-    std::function<void(size_t)> pre_parse_callback_;
+    std::function<void(std::size_t)> pre_parse_callback_{};
 
     /// This is a function that runs when parsing has finished.
-    std::function<void()> parse_complete_callback_;
+    std::function<void()> parse_complete_callback_{};
     /// This is a function that runs when all processing has completed
-    std::function<void()> final_callback_;
+    std::function<void()> final_callback_{};
 
     ///@}
     /// @name Options
     ///@{
 
     /// The default values for options, customizable and changeable INHERITABLE
-    OptionDefaults option_defaults_;
+    OptionDefaults option_defaults_{};
 
     /// The list of options, stored locally
-    std::vector<Option_p> options_;
+    std::vector<Option_p> options_{};
 
     ///@}
     /// @name Help
     ///@{
 
     /// Footer to put after all options in the help output INHERITABLE
-    std::string footer_;
+    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_;
+    std::function<std::string()> footer_callback_{};
 
     /// A pointer to the help flag if there is one INHERITABLE
     Option *help_ptr_{nullptr};
@@ -133,7 +143,7 @@ class App {
     std::shared_ptr<FormatterBase> formatter_{new Formatter()};
 
     /// The error message printing function INHERITABLE
-    std::function<std::string(const App *, const Error &e)> failure_message_ = FailureMessage::simple;
+    std::function<std::string(const App *, const Error &e)> failure_message_{FailureMessage::simple};
 
     ///@}
     /// @name Parsing
@@ -144,35 +154,35 @@ class App {
     /// Pair of classifier, string for missing options. (extra detail is removed on returning from parse)
     ///
     /// This is faster and cleaner than storing just a list of strings and reparsing. This may contain the -- separator.
-    missing_t missing_;
+    missing_t missing_{};
 
     /// This is a list of pointers to options with the original parse order
-    std::vector<Option *> parse_order_;
+    std::vector<Option *> parse_order_{};
 
     /// This is a list of the subcommands collected, in order
-    std::vector<App *> parsed_subcommands_;
+    std::vector<App *> parsed_subcommands_{};
 
     /// this is a list of subcommands that are exclusionary to this one
-    std::set<App *> exclude_subcommands_;
+    std::set<App *> exclude_subcommands_{};
 
     /// This is a list of options which are exclusionary to this App, if the options were used this subcommand should
     /// not be
-    std::set<Option *> exclude_options_;
+    std::set<Option *> exclude_options_{};
 
     /// this is a list of subcommands or option groups that are required by this one, the list is not mutual,  the
     /// listed subcommands do not require this one
-    std::set<App *> need_subcommands_;
+    std::set<App *> need_subcommands_{};
 
     /// This is a list of options which are required by this app, the list is not mutual, listed options do not need the
     /// subcommand not be
-    std::set<Option *> need_options_;
+    std::set<Option *> need_options_{};
 
     ///@}
     /// @name Subcommands
     ///@{
 
     /// Storage for subcommand list
-    std::vector<App_p> subcommands_;
+    std::vector<App_p> subcommands_{};
 
     /// If true, the program name is not case sensitive INHERITABLE
     bool ignore_case_{false};
@@ -194,46 +204,45 @@ class App {
     /// specify that positional arguments come at the end of the argument sequence not inheritable
     bool positionals_at_end_{false};
 
-    /// If set to true the subcommand will start each parse disabled
-    bool disabled_by_default_{false};
-    /// If set to true the subcommand will be reenabled at the start of each parse
-    bool enabled_by_default_{false};
+    enum class startup_mode : char { stable, enabled, disabled };
+    /// specify the startup mode for the app
+    /// stable=no change, enabled= startup enabled, disabled=startup disabled
+    startup_mode default_startup{startup_mode::stable};
+
+    /// if set to true the subcommand can be triggered via configuration files INHERITABLE
+    bool configurable_{false};
+
     /// If set to true positional options are validated before assigning INHERITABLE
     bool validate_positionals_{false};
+
     /// A pointer to the parent if this is a subcommand
     App *parent_{nullptr};
 
     /// Counts the number of times this command/subcommand was parsed
-    size_t parsed_ = 0;
+    std::size_t parsed_{0};
 
     /// Minimum required subcommands (not inheritable!)
-    size_t require_subcommand_min_ = 0;
+    std::size_t require_subcommand_min_{0};
 
     /// Max number of subcommands allowed (parsing stops after this number). 0 is unlimited INHERITABLE
-    size_t require_subcommand_max_ = 0;
+    std::size_t require_subcommand_max_{0};
 
     /// Minimum required options (not inheritable!)
-    size_t require_option_min_ = 0;
+    std::size_t require_option_min_{0};
 
     /// Max number of options allowed. 0 is unlimited (not inheritable)
-    size_t require_option_max_ = 0;
+    std::size_t require_option_max_{0};
 
     /// The group membership INHERITABLE
     std::string group_{"Subcommands"};
 
     /// Alias names for the subcommand
-    std::vector<std::string> aliases_;
+    std::vector<std::string> aliases_{};
 
     ///@}
     /// @name Config
     ///@{
 
-    /// The name of the connected config file
-    std::string config_name_;
-
-    /// True if ini is required (throws if not present), if false simply keep going.
-    bool config_required_{false};
-
     /// Pointer to the config option
     Option *config_ptr_{nullptr};
 
@@ -266,6 +275,7 @@ class App {
             ignore_underscore_ = parent_->ignore_underscore_;
             fallthrough_ = parent_->fallthrough_;
             validate_positionals_ = parent_->validate_positionals_;
+            configurable_ = parent_->configurable_;
             allow_windows_style_options_ = parent_->allow_windows_style_options_;
             group_ = parent_->group_;
             footer_ = parent_->footer_;
@@ -285,6 +295,9 @@ class App {
         set_help_flag("-h,--help", "Print this help message and exit");
     }
 
+    App(const App &) = delete;
+    App &operator=(const App &) = delete;
+
     /// virtual destructor
     virtual ~App() = default;
 
@@ -319,7 +332,7 @@ class App {
 
     /// Set a callback to execute prior to parsing.
     ///
-    App *preparse_callback(std::function<void(size_t)> pp_callback) {
+    App *preparse_callback(std::function<void(std::size_t)> pp_callback) {
         pre_parse_callback_ = std::move(pp_callback);
         return this;
     }
@@ -382,14 +395,23 @@ class App {
 
     /// Set the subcommand to be disabled by default, so on clear(), at the start of each parse it is disabled
     App *disabled_by_default(bool disable = true) {
-        disabled_by_default_ = disable;
+        if(disable) {
+            default_startup = startup_mode::disabled;
+        } else {
+            default_startup = (default_startup == startup_mode::enabled) ? startup_mode::enabled : startup_mode::stable;
+        }
         return this;
     }
 
     /// Set the subcommand to be enabled by default, so on clear(), at the start of each parse it is enabled (not
     /// disabled)
     App *enabled_by_default(bool enable = true) {
-        enabled_by_default_ = enable;
+        if(enable) {
+            default_startup = startup_mode::enabled;
+        } else {
+            default_startup =
+                (default_startup == startup_mode::disabled) ? startup_mode::disabled : startup_mode::stable;
+        }
         return this;
     }
 
@@ -412,11 +434,20 @@ class App {
         return this;
     }
 
-    /// Remove the error when extras are left over on the command line.
-    /// Will also call App::allow_extras().
+    /// ignore extras in config files
     App *allow_config_extras(bool allow = true) {
-        allow_extras(allow);
-        allow_config_extras_ = allow;
+        if(allow) {
+            allow_config_extras_ = config_extras_mode::capture;
+            allow_extras_ = true;
+        } else {
+            allow_config_extras_ = config_extras_mode::error;
+        }
+        return this;
+    }
+
+    /// ignore extras in config files
+    App *allow_config_extras(config_extras_mode mode) {
+        allow_config_extras_ = mode;
         return this;
     }
 
@@ -454,6 +485,12 @@ class App {
         return this;
     }
 
+    /// Specify that the subcommand can be triggered by a config file
+    App *configurable(bool value = true) {
+        configurable_ = value;
+        return this;
+    }
+
     /// Ignore underscore. Subcommands inherit value.
     App *ignore_underscore(bool value = true) {
         if(value && !ignore_underscore_) {
@@ -549,32 +586,33 @@ class App {
             }
         }
         // this line should not be reached the above loop should trigger the throw
-        // LCOV_EXCL_START
-        throw(OptionAlreadyAdded("added option matched existing option name"));
-        // LCOV_EXCL_END
+        throw(OptionAlreadyAdded("added option matched existing option name")); // LCOV_EXCL_LINE
     }
 
-    /// Add option for non-vectors (duplicate copy needed without defaulted to avoid `iostream << value`)
-
-    template <typename T, typename XC = T, enable_if_t<!std::is_const<XC>::value, detail::enabler> = detail::dummy>
+    /// Add option for assigning to a variable
+    template <typename AssignTo,
+              typename ConvertTo = AssignTo,
+              enable_if_t<!std::is_const<ConvertTo>::value, detail::enabler> = detail::dummy>
     Option *add_option(std::string option_name,
-                       T &variable, ///< The variable to set
+                       AssignTo &variable, ///< The variable to set
                        std::string option_description = "",
                        bool defaulted = false) {
 
-        auto fun = [&variable](CLI::results_t res) { // comment for spacing
-            return detail::lexical_conversion<T, XC>(res, variable);
+        auto fun = [&variable](const CLI::results_t &res) { // comment for spacing
+            return detail::lexical_conversion<AssignTo, ConvertTo>(res, variable);
         };
 
         Option *opt = add_option(option_name, fun, option_description, defaulted, [&variable]() {
-            return CLI::detail::checked_to_string<T, XC>(variable);
+            return CLI::detail::checked_to_string<AssignTo, ConvertTo>(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
+        opt->type_name(detail::type_name<ConvertTo>());
+        // these must be actual lvalues 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;
+        auto Tcount = detail::type_count<AssignTo>::value;
+        auto XCcount = detail::type_count<ConvertTo>::value;
         opt->type_size((std::max)(Tcount, XCcount));
+        opt->expected(detail::expected_count<ConvertTo>::value);
+        opt->run_callback_for_default();
         return opt;
     }
 
@@ -584,7 +622,7 @@ class App {
                                 const std::function<void(const T &)> &func, ///< the callback to execute
                                 std::string option_description = "") {
 
-        auto fun = [func](CLI::results_t res) {
+        auto fun = [func](const CLI::results_t &res) {
             T variable;
             bool result = detail::lexical_conversion<T, T>(res, variable);
             if(result) {
@@ -596,6 +634,7 @@ 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);
+        opt->expected(detail::expected_count<T>::value);
         return opt;
     }
 
@@ -667,8 +706,9 @@ class App {
             remove_option(opt);
             throw IncorrectConstruction::PositionalFlag(pos_name);
         }
-
-        opt->type_size(0);
+        opt->multi_option_policy(MultiOptionPolicy::TakeLast);
+        opt->expected(0);
+        opt->required(false);
         return opt;
     }
 
@@ -694,7 +734,7 @@ class App {
                      T &flag_count, ///< A variable holding the count
                      std::string flag_description = "") {
         flag_count = 0;
-        CLI::callback_t fun = [&flag_count](CLI::results_t res) {
+        CLI::callback_t fun = [&flag_count](const CLI::results_t &res) {
             try {
                 detail::sum_flag_vector(res, flag_count);
             } catch(const std::invalid_argument &) {
@@ -702,7 +742,8 @@ class App {
             }
             return true;
         };
-        return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
+        return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))
+            ->multi_option_policy(MultiOptionPolicy::TakeAll);
     }
 
     /// Other type version accepts all other types that are not vectors such as bool, enum, string or other classes
@@ -716,24 +757,20 @@ class App {
                      T &flag_result, ///< A variable holding true if passed
                      std::string flag_description = "") {
 
-        CLI::callback_t fun = [&flag_result](CLI::results_t res) {
-            if(res.size() != 1) {
-                return false;
-            }
+        CLI::callback_t fun = [&flag_result](const CLI::results_t &res) {
             return CLI::detail::lexical_cast(res[0], flag_result);
         };
-        Option *opt = _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
-        opt->multi_option_policy(CLI::MultiOptionPolicy::TakeLast);
-        return opt;
+        return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))->run_callback_for_default();
     }
 
     /// Vector version to capture multiple flags.
-    template <typename T,
-              enable_if_t<!std::is_assignable<std::function<void(int64_t)>, T>::value, detail::enabler> = detail::dummy>
+    template <
+        typename T,
+        enable_if_t<!std::is_assignable<std::function<void(std::int64_t)>, T>::value, detail::enabler> = detail::dummy>
     Option *add_flag(std::string flag_name,
                      std::vector<T> &flag_results, ///< A vector of values with the flag results
                      std::string flag_description = "") {
-        CLI::callback_t fun = [&flag_results](CLI::results_t res) {
+        CLI::callback_t fun = [&flag_results](const CLI::results_t &res) {
             bool retval = true;
             for(const auto &elem : res) {
                 flag_results.emplace_back();
@@ -741,7 +778,9 @@ class App {
             }
             return retval;
         };
-        return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
+        return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))
+            ->multi_option_policy(MultiOptionPolicy::TakeAll)
+            ->run_callback_for_default();
     }
 
     /// Add option for callback that is triggered with a true flag and takes no arguments
@@ -749,39 +788,36 @@ class App {
                               std::function<void(void)> function, ///< A function to call, void(void)
                               std::string flag_description = "") {
 
-        CLI::callback_t fun = [function](CLI::results_t res) {
-            if(res.size() != 1) {
-                return false;
-            }
-            bool trigger;
+        CLI::callback_t fun = [function](const CLI::results_t &res) {
+            bool trigger{false};
             auto result = CLI::detail::lexical_cast(res[0], trigger);
-            if(trigger)
+            if(result && trigger) {
                 function();
+            }
             return result;
         };
-        Option *opt = _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
-        opt->multi_option_policy(CLI::MultiOptionPolicy::TakeLast);
-        return opt;
+        return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
     }
 
     /// Add option for callback with an integer value
     Option *add_flag_function(std::string flag_name,
-                              std::function<void(int64_t)> function, ///< A function to call, void(int)
+                              std::function<void(std::int64_t)> function, ///< A function to call, void(int)
                               std::string flag_description = "") {
 
-        CLI::callback_t fun = [function](CLI::results_t res) {
-            int64_t flag_count = 0;
+        CLI::callback_t fun = [function](const CLI::results_t &res) {
+            std::int64_t flag_count = 0;
             detail::sum_flag_vector(res, flag_count);
             function(flag_count);
             return true;
         };
-        return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
+        return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))
+            ->multi_option_policy(MultiOptionPolicy::TakeAll);
     }
 
 #ifdef CLI11_CPP14
     /// Add option for callback (C++14 or better only)
     Option *add_flag(std::string flag_name,
-                     std::function<void(int64_t)> function, ///< A function to call, void(int64_t)
+                     std::function<void(std::int64_t)> function, ///< A function to call, void(std::int64_t)
                      std::string flag_description = "") {
         return add_flag_function(std::move(flag_name), std::move(function), std::move(flag_description));
     }
@@ -837,215 +873,77 @@ class App {
         return opt;
     }
 
-    /// Add set of options, string only, ignore case (no default, static set) DEPRECATED
-    CLI11_DEPRECATED("Use ->transform(CLI::IsMember(..., CLI::ignore_case)) instead")
-    Option *add_set_ignore_case(std::string option_name,
-                                std::string &member,           ///< The selected member of the set
-                                std::set<std::string> options, ///< The set of possibilities
-                                std::string option_description = "") {
-
-        Option *opt = add_option(option_name, member, std::move(option_description));
-        opt->transform(IsMember{options, CLI::ignore_case});
-        return opt;
-    }
-
-    /// Add set of options, string only, ignore case (no default, set can be changed afterwards - do not destroy the
-    /// set) DEPRECATED
-    CLI11_DEPRECATED("Use ->transform(CLI::IsMember(..., CLI::ignore_case)) with a (shared) pointer instead")
-    Option *add_mutable_set_ignore_case(std::string option_name,
-                                        std::string &member,                  ///< The selected member of the set
-                                        const std::set<std::string> &options, ///< The set of possibilities
-                                        std::string option_description = "") {
-
-        Option *opt = add_option(option_name, member, std::move(option_description));
-        opt->transform(IsMember{&options, CLI::ignore_case});
-        return opt;
-    }
-
-    /// Add set of options, string only, ignore case (default, static set) DEPRECATED
-    CLI11_DEPRECATED("Use ->transform(CLI::IsMember(..., CLI::ignore_case)) instead")
-    Option *add_set_ignore_case(std::string option_name,
-                                std::string &member,           ///< The selected member of the set
-                                std::set<std::string> options, ///< The set of possibilities
-                                std::string option_description,
-                                bool defaulted) {
-
-        Option *opt = add_option(option_name, member, std::move(option_description), defaulted);
-        opt->transform(IsMember{options, CLI::ignore_case});
-        return opt;
-    }
-
-    /// 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
-                                        const std::set<std::string> &options, ///< The set of possibilities
-                                        std::string option_description,
-                                        bool defaulted) {
-
-        Option *opt = add_option(option_name, member, std::move(option_description), defaulted);
-        opt->transform(IsMember{&options, CLI::ignore_case});
-        return opt;
-    }
-
-    /// Add set of options, string only, ignore underscore (no default, static set) DEPRECATED
-    CLI11_DEPRECATED("Use ->transform(CLI::IsMember(..., CLI::ignore_underscore)) instead")
-    Option *add_set_ignore_underscore(std::string option_name,
-                                      std::string &member,           ///< The selected member of the set
-                                      std::set<std::string> options, ///< The set of possibilities
-                                      std::string option_description = "") {
-
-        Option *opt = add_option(option_name, member, std::move(option_description));
-        opt->transform(IsMember{options, CLI::ignore_underscore});
-        return opt;
-    }
-
-    /// 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
-                                              const std::set<std::string> &options, ///< The set of possibilities
-                                              std::string option_description = "") {
-
-        Option *opt = add_option(option_name, member, std::move(option_description));
-        opt->transform(IsMember{options, CLI::ignore_underscore});
-        return opt;
-    }
-
-    /// Add set of options, string only, ignore underscore (default, static set) DEPRECATED
-    CLI11_DEPRECATED("Use ->transform(CLI::IsMember(..., CLI::ignore_underscore)) instead")
-    Option *add_set_ignore_underscore(std::string option_name,
-                                      std::string &member,           ///< The selected member of the set
-                                      std::set<std::string> options, ///< The set of possibilities
-                                      std::string option_description,
-                                      bool defaulted) {
-
-        Option *opt = add_option(option_name, member, std::move(option_description), defaulted);
-        opt->transform(IsMember{options, CLI::ignore_underscore});
-        return opt;
-    }
-
-    /// 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
-                                              const std::set<std::string> &options, ///< The set of possibilities
-                                              std::string option_description,
-                                              bool defaulted) {
-
-        Option *opt = add_option(option_name, member, std::move(option_description), defaulted);
-        opt->transform(IsMember{&options, CLI::ignore_underscore});
-        return opt;
-    }
-
-    /// Add set of options, string only, ignore underscore and case (no default, static set) DEPRECATED
-    CLI11_DEPRECATED("Use ->transform(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) instead")
-    Option *add_set_ignore_case_underscore(std::string option_name,
-                                           std::string &member,           ///< The selected member of the set
-                                           std::set<std::string> options, ///< The set of possibilities
-                                           std::string option_description = "") {
-
-        Option *opt = add_option(option_name, member, std::move(option_description));
-        opt->transform(IsMember{options, CLI::ignore_underscore, CLI::ignore_case});
-        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")
-    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
-                                                   std::string option_description = "") {
-
-        Option *opt = add_option(option_name, member, std::move(option_description));
-        opt->transform(IsMember{&options, CLI::ignore_underscore, CLI::ignore_case});
-        return opt;
-    }
-
-    /// Add set of options, string only, ignore underscore and case (default, static set) DEPRECATED
-    CLI11_DEPRECATED("Use ->transform(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) instead")
-    Option *add_set_ignore_case_underscore(std::string option_name,
-                                           std::string &member,           ///< The selected member of the set
-                                           std::set<std::string> options, ///< The set of possibilities
-                                           std::string option_description,
-                                           bool defaulted) {
-
-        Option *opt = add_option(option_name, member, std::move(option_description), defaulted);
-        opt->transform(IsMember{options, CLI::ignore_underscore, CLI::ignore_case});
-        return opt;
-    }
-
-    /// 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")
-    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
-                                                   std::string option_description,
-                                                   bool defaulted) {
-
-        Option *opt = add_option(option_name, member, std::move(option_description), defaulted);
-        opt->transform(IsMember{&options, CLI::ignore_underscore, CLI::ignore_case});
-        return opt;
-    }
-
     /// Add a complex number
-    template <typename T>
+    template <typename T, typename XC = double>
     Option *add_complex(std::string option_name,
                         T &variable,
                         std::string option_description = "",
                         bool defaulted = false,
                         std::string label = "COMPLEX") {
 
-        std::string simple_name = CLI::detail::split(option_name, ',').at(0);
-        CLI::callback_t fun = [&variable, simple_name, label](results_t res) {
-            if(res[1].back() == 'i')
-                res[1].pop_back();
-            double x, y;
-            bool worked = detail::lexical_cast(res[0], x) && detail::lexical_cast(res[1], y);
+        CLI::callback_t fun = [&variable](const results_t &res) {
+            XC x, y;
+            bool worked;
+            if(res.size() >= 2 && !res[1].empty()) {
+                auto str1 = res[1];
+                if(str1.back() == 'i' || str1.back() == 'j')
+                    str1.pop_back();
+                worked = detail::lexical_cast(res[0], x) && detail::lexical_cast(str1, y);
+            } else {
+                auto str1 = res.front();
+                auto nloc = str1.find_last_of('-');
+                if(nloc != std::string::npos && nloc > 0) {
+                    worked = detail::lexical_cast(str1.substr(0, nloc), x);
+                    str1 = str1.substr(nloc);
+                    if(str1.back() == 'i' || str1.back() == 'j')
+                        str1.pop_back();
+                    worked = worked && detail::lexical_cast(str1, y);
+                } else {
+                    if(str1.back() == 'i' || str1.back() == 'j') {
+                        str1.pop_back();
+                        worked = detail::lexical_cast(str1, y);
+                        x = XC{0};
+                    } else {
+                        worked = detail::lexical_cast(str1, x);
+                        y = XC{0};
+                    }
+                }
+            }
             if(worked)
-                variable = T(x, y);
+                variable = T{x, y};
             return worked;
         };
 
-        auto default_function = [&variable]() {
-            std::stringstream out;
-            out << variable;
-            return out.str();
-        };
+        auto default_function = [&variable]() { return CLI::detail::checked_to_string<T, T>(variable); };
 
         CLI::Option *opt =
             add_option(option_name, std::move(fun), std::move(option_description), defaulted, default_function);
 
-        opt->type_name(label)->type_size(2);
+        opt->type_name(label)->type_size(1, 2)->delimiter('+')->run_callback_for_default();
         return opt;
     }
 
     /// Set a configuration ini file option, or clear it if no name passed
     Option *set_config(std::string option_name = "",
                        std::string default_filename = "",
-                       std::string help_message = "Read an ini file",
+                       const std::string &help_message = "Read an ini file",
                        bool config_required = false) {
 
         // Remove existing config if present
         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
+            config_ptr_ = nullptr; // need to remove the config_ptr completely
         }
 
         // Only add config if option passed
         if(!option_name.empty()) {
-            config_name_ = default_filename;
-            config_required_ = config_required;
-            config_ptr_ = add_option(option_name, config_name_, help_message, !default_filename.empty());
+            config_ptr_ = add_option(option_name, help_message);
+            if(config_required) {
+                config_ptr_->required();
+            }
+            if(!default_filename.empty()) {
+                config_ptr_->default_str(std::move(default_filename));
+            }
             config_ptr_->configurable(false);
         }
 
@@ -1130,12 +1028,12 @@ class App {
     }
     /// Check to see if a subcommand is part of this command (doesn't have to be in command line)
     /// returns the first subcommand if passed a nullptr
-    App *get_subcommand(App *subcom) const {
+    App *get_subcommand(const App *subcom) const {
         if(subcom == nullptr)
             throw OptionNotFound("nullptr passed");
         for(const App_p &subcomptr : subcommands_)
             if(subcomptr.get() == subcom)
-                return subcom;
+                return subcomptr.get();
         throw OptionNotFound(subcom->get_name());
     }
 
@@ -1197,12 +1095,12 @@ class App {
     /// No argument version of count counts the number of times this subcommand was
     /// passed in. The main app will return 1. Unnamed subcommands will also return 1 unless
     /// otherwise modified in a callback
-    size_t count() const { return parsed_; }
+    std::size_t count() const { return parsed_; }
 
     /// Get a count of all the arguments processed in options and subcommands, this excludes arguments which were
     /// treated as extras.
-    size_t count_all() const {
-        size_t cnt{0};
+    std::size_t count_all() const {
+        std::size_t cnt{0};
         for(auto &opt : options_) {
             cnt += opt->count();
         }
@@ -1234,17 +1132,17 @@ class App {
     App *require_subcommand(int value) {
         if(value < 0) {
             require_subcommand_min_ = 0;
-            require_subcommand_max_ = static_cast<size_t>(-value);
+            require_subcommand_max_ = static_cast<std::size_t>(-value);
         } else {
-            require_subcommand_min_ = static_cast<size_t>(value);
-            require_subcommand_max_ = static_cast<size_t>(value);
+            require_subcommand_min_ = static_cast<std::size_t>(value);
+            require_subcommand_max_ = static_cast<std::size_t>(value);
         }
         return this;
     }
 
     /// Explicitly control the number of subcommands required. Setting 0
     /// for the max means unlimited number allowed. Max number inheritable.
-    App *require_subcommand(size_t min, size_t max) {
+    App *require_subcommand(std::size_t min, std::size_t max) {
         require_subcommand_min_ = min;
         require_subcommand_max_ = max;
         return this;
@@ -1263,17 +1161,17 @@ class App {
     App *require_option(int value) {
         if(value < 0) {
             require_option_min_ = 0;
-            require_option_max_ = static_cast<size_t>(-value);
+            require_option_max_ = static_cast<std::size_t>(-value);
         } else {
-            require_option_min_ = static_cast<size_t>(value);
-            require_option_max_ = static_cast<size_t>(value);
+            require_option_min_ = static_cast<std::size_t>(value);
+            require_option_max_ = static_cast<std::size_t>(value);
         }
         return this;
     }
 
     /// Explicitly control the number of options required. Setting 0
     /// for the max means unlimited number allowed. Max number inheritable.
-    App *require_option(size_t min, size_t max) {
+    App *require_option(std::size_t min, std::size_t max) {
         require_option_min_ = min;
         require_option_max_ = max;
         return this;
@@ -1288,7 +1186,7 @@ class App {
 
     /// Check to see if this subcommand was parsed, true only if received on command line.
     /// This allows the subcommand to be directly checked.
-    operator bool() const { return parsed_ > 0; }
+    explicit operator bool() const { return parsed_ > 0; }
 
     ///@}
     /// @name Extras for subclassing
@@ -1329,7 +1227,7 @@ class App {
         }
 
         std::vector<std::string> args;
-        args.reserve(static_cast<size_t>(argc) - 1);
+        args.reserve(static_cast<std::size_t>(argc) - 1);
         for(int i = argc - 1; i > 0; i--)
             args.emplace_back(argv[i]);
         parse(std::move(args));
@@ -1441,7 +1339,7 @@ class App {
     ///@{
 
     /// Counts the number of times the given option was passed.
-    size_t count(std::string option_name) const { return get_option(option_name)->count(); }
+    std::size_t count(std::string option_name) const { return get_option(option_name)->count(); }
 
     /// Get a subcommand pointer list to the currently selected subcommands (after parsing by default, in command
     /// line order; use parsed = false to get the original definition list.)
@@ -1483,7 +1381,7 @@ class App {
     }
 
     /// Check to see if given subcommand was selected
-    bool got_subcommand(App *subcom) const {
+    bool got_subcommand(const App *subcom) const {
         // get subcom needed to verify that this was a real subcommand
         return get_subcommand(subcom)->parsed_ > 0;
     }
@@ -1623,6 +1521,11 @@ class App {
     /// Access the config formatter
     std::shared_ptr<Config> get_config_formatter() const { return config_formatter_; }
 
+    /// Access the config formatter as a configBase pointer
+    std::shared_ptr<ConfigBase> get_config_formatter_base() const {
+        return std::dynamic_pointer_cast<ConfigBase>(config_formatter_);
+    }
+
     /// Get the app or subcommand description
     std::string get_description() const { return description_; }
 
@@ -1742,6 +1645,9 @@ class App {
     /// Check the status of the allow windows style options
     bool get_positionals_at_end() const { return positionals_at_end_; }
 
+    /// Check the status of the allow windows style options
+    bool get_configurable() const { return configurable_; }
+
     /// Get the group of this subcommand
     const std::string &get_group() const { return group_; }
 
@@ -1749,16 +1655,16 @@ class App {
     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_; }
+    std::size_t get_require_subcommand_min() const { return require_subcommand_min_; }
 
     /// Get the required max subcommand value
-    size_t get_require_subcommand_max() const { return require_subcommand_max_; }
+    std::size_t get_require_subcommand_max() const { return require_subcommand_max_; }
 
     /// Get the required min option value
-    size_t get_require_option_min() const { return require_option_min_; }
+    std::size_t get_require_option_min() const { return require_option_min_; }
 
     /// Get the required max option value
-    size_t get_require_option_max() const { return require_option_max_; }
+    std::size_t get_require_option_max() const { return require_option_max_; }
 
     /// Get the prefix command status
     bool get_prefix_command() const { return prefix_command_; }
@@ -1776,15 +1682,15 @@ class App {
     bool get_immediate_callback() const { return immediate_callback_; }
 
     /// Get the status of disabled by default
-    bool get_disabled_by_default() const { return disabled_by_default_; }
+    bool get_disabled_by_default() const { return (default_startup == startup_mode::disabled); }
 
     /// Get the status of disabled by default
-    bool get_enabled_by_default() const { return enabled_by_default_; }
+    bool get_enabled_by_default() const { return (default_startup == startup_mode::enabled); }
     /// Get the status of validating positionals
     bool get_validate_positionals() const { return validate_positionals_; }
 
     /// Get the status of allow extras
-    bool get_allow_config_extras() const { return allow_config_extras_; }
+    config_extras_mode get_allow_config_extras() const { return allow_config_extras_; }
 
     /// Get a pointer to the help flag.
     Option *get_help_ptr() { return help_ptr_; }
@@ -1903,8 +1809,8 @@ class App {
     }
 
     /// 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(
+    std::size_t remaining_size(bool recurse = false) const {
+        auto remaining_options = static_cast<std::size_t>(std::count_if(
             std::begin(missing_), std::end(missing_), [](const std::pair<detail::Classifier, std::string> &val) {
                 return val.first != detail::Classifier::POSITIONAL_MARK;
             }));
@@ -1922,16 +1828,24 @@ class App {
   protected:
     /// Check the options to make sure there are no conflicts.
     ///
-    /// Currently checks to see if multiple positionals exist with -1 args and checks if the min and max options are
-    /// feasible
+    /// Currently checks to see if multiple positionals exist with unlimited args and checks if the min and max options
+    /// are feasible
     void _validate() const {
+        // count the number of positional only args
         auto pcount = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
-            return opt->get_items_expected() < 0 && opt->get_positional();
+            return opt->get_items_expected_max() >= detail::expected_max_vector_size && !opt->nonpositional();
         });
-        if(pcount > 1)
-            throw InvalidError(name_);
+        if(pcount > 1) {
+            auto pcount_req = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
+                return opt->get_items_expected_max() >= detail::expected_max_vector_size && !opt->nonpositional() &&
+                       opt->get_required();
+            });
+            if(pcount - pcount_req > 1) {
+                throw InvalidError(name_);
+            }
+        }
 
-        size_t nameless_subs{0};
+        std::size_t nameless_subs{0};
         for(const App_p &app : subcommands_) {
             app->_validate();
             if(app->get_name().empty())
@@ -1956,11 +1870,10 @@ class App {
     /// set the correct fallthrough and prefix for nameless subcommands and manage the automatic enable or disable
     /// makes sure parent is set correctly
     void _configure() {
-        if(disabled_by_default_) {
-            disabled_ = true;
-        }
-        if(enabled_by_default_) {
+        if(default_startup == startup_mode::enabled) {
             disabled_ = false;
+        } else if(default_startup == startup_mode::disabled) {
+            disabled_ = true;
         }
         for(const App_p &app : subcommands_) {
             if(app->has_automatic_name_) {
@@ -2042,22 +1955,33 @@ class App {
 
     // The parse function is now broken into several parts, and part of process
 
-    /// Read and process an ini file (main app only)
-    void _process_ini() {
-        // Process an INI file
+    /// Read and process a configuration file (main app only)
+    void _process_config_file() {
         if(config_ptr_ != nullptr) {
-            if(*config_ptr_) {
-                config_ptr_->run_callback();
-                config_required_ = true;
+            bool config_required = config_ptr_->get_required();
+            bool file_given = config_ptr_->count() > 0;
+            auto config_file = config_ptr_->as<std::string>();
+            if(config_file.empty()) {
+                if(config_required) {
+                    throw FileError::Missing("no specified config file");
+                }
+                return;
             }
-            if(!config_name_.empty()) {
+
+            auto path_result = detail::check_path(config_file.c_str());
+            if(path_result == detail::path_type::file) {
                 try {
-                    std::vector<ConfigItem> values = config_formatter_->from_file(config_name_);
+                    std::vector<ConfigItem> values = config_formatter_->from_file(config_file);
                     _parse_config(values);
+                    if(!file_given) {
+                        config_ptr_->add_result(config_file);
+                    }
                 } catch(const FileError &) {
-                    if(config_required_)
+                    if(config_required || file_given)
                         throw;
                 }
+            } else if(config_required || file_given) {
+                throw FileError::Missing(config_file);
             }
         }
     }
@@ -2071,7 +1995,7 @@ class App {
 
 #ifdef _MSC_VER
                 // Windows version
-                size_t sz = 0;
+                std::size_t sz = 0;
                 if(_dupenv_s(&buffer, &sz, opt->envname_.c_str()) == 0 && buffer != nullptr) {
                     ename_string = std::string(buffer);
                     free(buffer);
@@ -2193,21 +2117,15 @@ class App {
             return;
         }
 
-        size_t used_options = 0;
+        std::size_t used_options = 0;
         for(const Option_p &opt : options_) {
 
             if(opt->count() != 0) {
                 ++used_options;
             }
-            // Required or partially filled
-            if(opt->get_required() || opt->count() != 0) {
-                // Make sure enough -N arguments parsed (+N is already handled in parsing function)
-                if(opt->get_items_expected() < 0 && opt->count() < static_cast<size_t>(-opt->get_items_expected()))
-                    throw ArgumentMismatch::AtLeast(opt->get_name(), -opt->get_items_expected());
-
-                // Required but empty
-                if(opt->get_required() && opt->count() == 0)
-                    throw RequiredError(opt->get_name());
+            // Required but empty
+            if(opt->get_required() && opt->count() == 0) {
+                throw RequiredError(opt->get_name());
             }
             // Requires
             for(const Option *opt_req : opt->needs_)
@@ -2279,7 +2197,7 @@ class App {
 
     /// Process callbacks and such.
     void _process() {
-        _process_ini();
+        _process_config_file();
         _process_env();
         _process_callbacks();
         _process_help_flags();
@@ -2289,7 +2207,7 @@ class App {
     /// 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();
+            std::size_t num_left_over = remaining_size();
             if(num_left_over > 0) {
                 throw ExtrasError(name_, remaining(false));
             }
@@ -2305,7 +2223,7 @@ class App {
     /// Modifies the args to fill in the missing items before throwing.
     void _process_extras(std::vector<std::string> &args) {
         if(!(allow_extras_ || prefix_command_)) {
-            size_t num_left_over = remaining_size();
+            std::size_t num_left_over = remaining_size();
             if(num_left_over > 0) {
                 args = remaining(false);
                 throw ExtrasError(name_, args);
@@ -2378,26 +2296,47 @@ class App {
     /// Returns true if it managed to find the option, if false you'll need to remove the arg manually.
     void _parse_config(std::vector<ConfigItem> &args) {
         for(ConfigItem item : args) {
-            if(!_parse_single_config(item) && !allow_config_extras_)
+            if(!_parse_single_config(item) && allow_config_extras_ == config_extras_mode::error)
                 throw ConfigError::Extras(item.fullname());
         }
     }
 
     /// Fill in a single config option
-    bool _parse_single_config(const ConfigItem &item, size_t level = 0) {
+    bool _parse_single_config(const ConfigItem &item, std::size_t level = 0) {
         if(level < item.parents.size()) {
             try {
                 auto subcom = get_subcommand(item.parents.at(level));
-                return subcom->_parse_single_config(item, level + 1);
+                auto result = subcom->_parse_single_config(item, level + 1);
+
+                return result;
             } catch(const OptionNotFound &) {
                 return false;
             }
         }
-
+        // check for section open
+        if(item.name == "++") {
+            if(configurable_) {
+                increment_parsed();
+                _trigger_pre_parse(2);
+                if(parent_ != nullptr) {
+                    parent_->parsed_subcommands_.push_back(this);
+                }
+            }
+            return true;
+        }
+        // check for section close
+        if(item.name == "--") {
+            if(configurable_) {
+                _process_callbacks();
+                _process_requirements();
+                run_callback();
+            }
+            return true;
+        }
         Option *op = get_option_no_throw("--" + item.name);
         if(op == nullptr) {
             // If the option was not present
-            if(get_allow_config_extras())
+            if(get_allow_config_extras() == config_extras_mode::capture)
                 // Should we worry about classifying the extras properly?
                 missing_.emplace_back(detail::Classifier::NONE, item.fullname());
             return false;
@@ -2408,7 +2347,7 @@ class App {
 
         if(op->empty()) {
             // Flag parsing
-            if(op->get_type_size() == 0) {
+            if(op->get_expected_min() == 0) {
                 auto res = config_formatter_->to_flag(item);
                 res = op->get_flag_value(item.name, res);
 
@@ -2459,24 +2398,22 @@ class App {
                 positional_only = true;
             }
             break;
-
             // LCOV_EXCL_START
         default:
             throw HorribleError("unrecognized classifier (you should not see this!)");
-            // LCOV_EXCL_END
+            // LCOV_EXCL_STOP
         }
         return retval;
     }
 
     /// Count the required remaining positional arguments
-    size_t _count_remaining_positionals(bool required_only = false) const {
-        size_t retval = 0;
+    std::size_t _count_remaining_positionals(bool required_only = false) const {
+        std::size_t retval = 0;
         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;
+                if(opt->get_items_expected_min() > 0 &&
+                   static_cast<int>(opt->count()) < opt->get_items_expected_min()) {
+                    retval += static_cast<std::size_t>(opt->get_items_expected_min()) - opt->count();
                 }
             }
         }
@@ -2485,10 +2422,11 @@ class App {
 
     /// Count the required remaining positional arguments
     bool _has_remaining_positionals() const {
-        for(const Option_p &opt : options_)
-            if(opt->get_positional() &&
-               ((opt->get_items_expected() < 0) || ((static_cast<int>(opt->count()) < opt->get_items_expected()))))
+        for(const Option_p &opt : options_) {
+            if(opt->get_positional() && ((static_cast<int>(opt->count()) < opt->get_items_expected_min()))) {
                 return true;
+            }
+        }
 
         return false;
     }
@@ -2507,8 +2445,7 @@ class App {
             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(static_cast<int>(opt->count()) < opt->get_items_expected_min()) {
                             if(validate_positionals_) {
                                 std::string pos = positional;
                                 pos = opt->_validate(pos, 0);
@@ -2528,7 +2465,7 @@ class App {
         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)) {
+               (static_cast<int>(opt->count()) < opt->get_items_expected_min() || opt->get_allow_extra_args())) {
                 if(validate_positionals_) {
                     std::string pos = positional;
                     pos = opt->_validate(pos, 0);
@@ -2668,6 +2605,7 @@ class App {
                 throw HorribleError("windows option parsed but missing! You should not see this");
             break;
         case detail::Classifier::SUBCOMMAND:
+        case detail::Classifier::SUBCOMMAND_TERMINATOR:
         case detail::Classifier::POSITIONAL_MARK:
         case detail::Classifier::NONE:
         default:
@@ -2714,47 +2652,52 @@ class App {
         // Get a reference to the pointer to make syntax bearable
         Option_p &op = *op_ptr;
 
-        int num = op->get_items_expected();
+        int min_num = (std::min)(op->get_type_size_min(), op->get_items_expected_min());
+        int max_num = op->get_items_expected_max();
 
         // Make sure we always eat the minimum for unlimited vectors
-        int collected = 0;
-        int result_count = 0;
-        // deal with flag like things
-        if(num == 0) {
+        int collected = 0;    // total number of arguments collected
+        int result_count = 0; // local variable for number of results in a single arg string
+        // deal with purely flag like things
+        if(max_num == 0) {
             auto res = op->get_flag_value(arg_name, value);
             op->add_result(res);
             parse_order_.push_back(op.get());
-        }
-        // --this=value
-        else if(!value.empty()) {
+        } else if(!value.empty()) { // --this=value
             op->add_result(value, result_count);
             parse_order_.push_back(op.get());
             collected += result_count;
-            // If exact number expected
-            if(num > 0)
-                num = (num >= result_count) ? num - result_count : 0;
-
             // -Trest
         } else if(!rest.empty()) {
             op->add_result(rest, result_count);
             parse_order_.push_back(op.get());
             rest = "";
             collected += result_count;
-            // If exact number expected
-            if(num > 0)
-                num = (num >= result_count) ? num - result_count : 0;
         }
 
-        // Unlimited vector parser
-        if(num < 0) {
-            while(!args.empty() && _recognize(args.back(), false) == detail::Classifier::NONE) {
-                if(collected >= -num) {
-                    // We could break here for allow extras, but we don't
+        // gather the minimum number of arguments
+        while(min_num > collected && !args.empty()) {
+            std::string current_ = args.back();
+            args.pop_back();
+            op->add_result(current_, result_count);
+            parse_order_.push_back(op.get());
+            collected += result_count;
+        }
+
+        if(min_num > collected) { // if we have run out of arguments and the minimum was not met
+            throw ArgumentMismatch::TypedAtLeast(op->get_name(), min_num, op->get_type_name());
+        }
 
-                    // If any positionals remain, don't keep eating
-                    if(_count_remaining_positionals() > 0)
-                        break;
+        if(max_num > collected || op->get_allow_extra_args()) { // we allow optional arguments
+            auto remreqpos = _count_remaining_positionals(true);
+            // we have met the minimum now optionally check up to the maximum
+            while((collected < max_num || op->get_allow_extra_args()) && !args.empty() &&
+                  _recognize(args.back(), false) == detail::Classifier::NONE) {
+                // If any required positionals remain, don't keep eating
+                if(remreqpos >= args.size()) {
+                    break;
                 }
+
                 op->add_result(args.back(), result_count);
                 parse_order_.push_back(op.get());
                 args.pop_back();
@@ -2764,19 +2707,17 @@ class App {
             // Allow -- to end an unlimited list and "eat" it
             if(!args.empty() && _recognize(args.back()) == detail::Classifier::POSITIONAL_MARK)
                 args.pop_back();
-
-        } else {
-            while(num > 0 && !args.empty()) {
-                std::string current_ = args.back();
-                args.pop_back();
-                op->add_result(current_, result_count);
+            // optional flag that didn't receive anything now get the default value
+            if(min_num == 0 && max_num > 0 && collected == 0) {
+                auto res = op->get_flag_value(arg_name, std::string{});
+                op->add_result(res);
                 parse_order_.push_back(op.get());
-                num -= result_count;
             }
+        }
 
-            if(num > 0) {
-                throw ArgumentMismatch::TypedAtLeast(op->get_name(), num, op->get_type_name());
-            }
+        // if we only partially completed a type then add an empty string for later processing
+        if(min_num > 0 && op->get_type_size_max() != min_num && collected % op->get_type_size_max() != 0) {
+            op->add_result(std::string{});
         }
 
         if(!rest.empty()) {
@@ -2787,7 +2728,7 @@ class App {
     }
 
     /// Trigger the pre_parse callback if needed
-    void _trigger_pre_parse(size_t remaining_args) {
+    void _trigger_pre_parse(std::size_t remaining_args) {
         if(!pre_parse_called_) {
             pre_parse_called_ = true;
             if(pre_parse_callback_) {
@@ -2963,7 +2904,7 @@ class Option_group : public App {
 inline void TriggerOn(App *trigger_app, App *app_to_enable) {
     app_to_enable->enabled_by_default(false);
     app_to_enable->disabled_by_default();
-    trigger_app->preparse_callback([app_to_enable](size_t) { app_to_enable->disabled(false); });
+    trigger_app->preparse_callback([app_to_enable](std::size_t) { app_to_enable->disabled(false); });
 }
 
 /// Helper function to enable one option group/subcommand when another is used
@@ -2973,7 +2914,7 @@ inline void TriggerOn(App *trigger_app, std::vector<App *> apps_to_enable) {
         app->disabled_by_default();
     }
 
-    trigger_app->preparse_callback([apps_to_enable](size_t) {
+    trigger_app->preparse_callback([apps_to_enable](std::size_t) {
         for(auto &app : apps_to_enable) {
             app->disabled(false);
         }
@@ -2984,7 +2925,7 @@ inline void TriggerOn(App *trigger_app, std::vector<App *> apps_to_enable) {
 inline void TriggerOff(App *trigger_app, App *app_to_enable) {
     app_to_enable->disabled_by_default(false);
     app_to_enable->enabled_by_default();
-    trigger_app->preparse_callback([app_to_enable](size_t) { app_to_enable->disabled(); });
+    trigger_app->preparse_callback([app_to_enable](std::size_t) { app_to_enable->disabled(); });
 }
 
 /// Helper function to disable one option group/subcommand when another is used
@@ -2994,13 +2935,92 @@ inline void TriggerOff(App *trigger_app, std::vector<App *> apps_to_enable) {
         app->enabled_by_default();
     }
 
-    trigger_app->preparse_callback([apps_to_enable](size_t) {
+    trigger_app->preparse_callback([apps_to_enable](std::size_t) {
         for(auto &app : apps_to_enable) {
             app->disabled();
         }
     });
 }
 
+/// Helper function to mark an option as deprecated
+inline void deprecate_option(Option *opt, const std::string &replacement = "") {
+    Validator deprecate_warning{[opt, replacement](std::string &) {
+                                    std::cout << opt->get_name() << " is deprecated please use '" << replacement
+                                              << "' instead\n";
+                                    return std::string();
+                                },
+                                "DEPRECATED"};
+    deprecate_warning.application_index(0);
+    opt->check(deprecate_warning);
+    if(!replacement.empty()) {
+        opt->description(opt->get_description() + " DEPRECATED: please use '" + replacement + "' instead");
+    }
+}
+
+/// Helper function to mark an option as deprecated
+inline void deprecate_option(App *app, const std::string &option_name, const std::string &replacement = "") {
+    auto opt = app->get_option(option_name);
+    deprecate_option(opt, replacement);
+}
+
+/// Helper function to mark an option as deprecated
+inline void deprecate_option(App &app, const std::string &option_name, const std::string &replacement = "") {
+    auto opt = app.get_option(option_name);
+    deprecate_option(opt, replacement);
+}
+
+/// Helper function to mark an option as retired
+inline void retire_option(App *app, Option *opt) {
+    App temp;
+    auto option_copy = temp.add_option(opt->get_name(false, true))
+                           ->type_size(opt->get_type_size_min(), opt->get_type_size_max())
+                           ->expected(opt->get_expected_min(), opt->get_expected_max())
+                           ->allow_extra_args(opt->get_allow_extra_args());
+
+    app->remove_option(opt);
+    auto opt2 = app->add_option(option_copy->get_name(false, true), "option has been retired and has no effect")
+                    ->type_name("RETIRED")
+                    ->default_str("RETIRED")
+                    ->type_size(option_copy->get_type_size_min(), option_copy->get_type_size_max())
+                    ->expected(option_copy->get_expected_min(), option_copy->get_expected_max())
+                    ->allow_extra_args(option_copy->get_allow_extra_args());
+
+    Validator retired_warning{[opt2](std::string &) {
+                                  std::cout << "WARNING " << opt2->get_name() << " is retired and has no effect\n";
+                                  return std::string();
+                              },
+                              ""};
+    retired_warning.application_index(0);
+    opt2->check(retired_warning);
+}
+
+/// Helper function to mark an option as retired
+inline void retire_option(App &app, Option *opt) { retire_option(&app, opt); }
+
+/// Helper function to mark an option as retired
+inline void retire_option(App *app, const std::string &option_name) {
+
+    auto opt = app->get_option_no_throw(option_name);
+    if(opt != nullptr) {
+        retire_option(app, opt);
+        return;
+    }
+    auto opt2 = app->add_option(option_name, "option has been retired and has no effect")
+                    ->type_name("RETIRED")
+                    ->expected(0, 1)
+                    ->default_str("RETIRED");
+    Validator retired_warning{[opt2](std::string &) {
+                                  std::cout << "WARNING " << opt2->get_name() << " is retired and has no effect\n";
+                                  return std::string();
+                              },
+                              ""};
+    retired_warning.application_index(0);
+    opt2->check(retired_warning);
+}
+
+/// Helper function to mark an option as retired
+inline void retire_option(App &app, const std::string &option_name) { retire_option(&app, option_name); }
+
 namespace FailureMessage {
 
 /// Printout a clean, simple message on error (the default in CLI11 1.5+)
diff --git a/packages/CLI11/include/CLI/CLI.hpp b/packages/CLI11/include/CLI/CLI.hpp
index c2dbe7c5f304a00e3ea548e0aeda76ff21eba1cf..22e85aed184e7b443927617231f190235e47e41e 100644
--- a/packages/CLI11/include/CLI/CLI.hpp
+++ b/packages/CLI11/include/CLI/CLI.hpp
@@ -1,7 +1,10 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
 // CLI Library includes
 // Order is important for combiner script
diff --git a/packages/CLI11/include/CLI/Config.hpp b/packages/CLI11/include/CLI/Config.hpp
index d3e0f2b9e3b21d58f920102d63fabe295dc33f46..eafb6218a51609d268dfd119fe7d6e7dbcfa7694 100644
--- a/packages/CLI11/include/CLI/Config.hpp
+++ b/packages/CLI11/include/CLI/Config.hpp
@@ -1,12 +1,17 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
 #include <algorithm>
 #include <fstream>
 #include <iostream>
 #include <string>
+#include <utility>
+#include <vector>
 
 #include "CLI/App.hpp"
 #include "CLI/ConfigFwd.hpp"
@@ -14,58 +19,326 @@
 
 namespace CLI {
 
+namespace detail {
+
+inline std::string convert_arg_for_ini(const std::string &arg) {
+    if(arg.empty()) {
+        return std::string(2, '"');
+    }
+    // some specifically supported strings
+    if(arg == "true" || arg == "false" || arg == "nan" || arg == "inf") {
+        return arg;
+    }
+    // floating point conversion can convert some hex codes, but don't try that here
+    if(arg.compare(0, 2, "0x") != 0 && arg.compare(0, 2, "0X") != 0) {
+        double val;
+        if(detail::lexical_cast(arg, val)) {
+            return arg;
+        }
+    }
+    // just quote a single non numeric character
+    if(arg.size() == 1) {
+        return std::string("'") + arg + '\'';
+    }
+    // handle hex, binary or octal arguments
+    if(arg.front() == '0') {
+        if(arg[1] == 'x') {
+            if(std::all_of(arg.begin() + 2, arg.end(), [](char x) {
+                   return (x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f');
+               })) {
+                return arg;
+            }
+        } else if(arg[1] == 'o') {
+            if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x >= '0' && x <= '7'); })) {
+                return arg;
+            }
+        } else if(arg[1] == 'b') {
+            if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x == '0' || x == '1'); })) {
+                return arg;
+            }
+        }
+    }
+    if(arg.find_first_of('"') == std::string::npos) {
+        return std::string("\"") + arg + '"';
+    } else {
+        return std::string("'") + arg + '\'';
+    }
+}
+
+/// Comma separated join, adds quotes if needed
+inline std::string
+ini_join(const std::vector<std::string> &args, char sepChar = ',', char arrayStart = '[', char arrayEnd = ']') {
+    std::string joined;
+    if(args.size() > 1 && arrayStart != '\0') {
+        joined.push_back(arrayStart);
+    }
+    std::size_t start = 0;
+    for(const auto &arg : args) {
+        if(start++ > 0) {
+            joined.push_back(sepChar);
+            if(isspace(sepChar) == 0) {
+                joined.push_back(' ');
+            }
+        }
+        joined.append(convert_arg_for_ini(arg));
+    }
+    if(args.size() > 1 && arrayEnd != '\0') {
+        joined.push_back(arrayEnd);
+    }
+    return joined;
+}
+
+inline std::vector<std::string> generate_parents(const std::string &section, std::string &name) {
+    std::vector<std::string> parents;
+    if(detail::to_lower(section) != "default") {
+        if(section.find('.') != std::string::npos) {
+            parents = detail::split(section, '.');
+        } else {
+            parents = {section};
+        }
+    }
+    if(name.find('.') != std::string::npos) {
+        std::vector<std::string> plist = detail::split(name, '.');
+        name = plist.back();
+        detail::remove_quotes(name);
+        plist.pop_back();
+        parents.insert(parents.end(), plist.begin(), plist.end());
+    }
+
+    // clean up quotes on the parents
+    for(auto &parent : parents) {
+        detail::remove_quotes(parent);
+    }
+    return parents;
+}
+
+/// assuming non default segments do a check on the close and open of the segments in a configItem structure
+inline void checkParentSegments(std::vector<ConfigItem> &output, const std::string &currentSection) {
+
+    std::string estring;
+    auto parents = detail::generate_parents(currentSection, estring);
+    if(!output.empty() && output.back().name == "--") {
+        std::size_t msize = (parents.size() > 1U) ? parents.size() : 2;
+        while(output.back().parents.size() >= msize) {
+            output.push_back(output.back());
+            output.back().parents.pop_back();
+        }
+
+        if(parents.size() > 1) {
+            std::size_t common = 0;
+            std::size_t mpair = (std::min)(output.back().parents.size(), parents.size() - 1);
+            for(std::size_t ii = 0; ii < mpair; ++ii) {
+                if(output.back().parents[ii] != parents[ii]) {
+                    break;
+                }
+                ++common;
+            }
+            if(common == mpair) {
+                output.pop_back();
+            } else {
+                while(output.back().parents.size() > common + 1) {
+                    output.push_back(output.back());
+                    output.back().parents.pop_back();
+                }
+            }
+            for(std::size_t ii = common; ii < parents.size() - 1; ++ii) {
+                output.emplace_back();
+                output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
+                output.back().name = "++";
+            }
+        }
+    } else if(parents.size() > 1) {
+        for(std::size_t ii = 0; ii < parents.size() - 1; ++ii) {
+            output.emplace_back();
+            output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
+            output.back().name = "++";
+        }
+    }
+
+    // insert a section end which is just an empty items_buffer
+    output.emplace_back();
+    output.back().parents = std::move(parents);
+    output.back().name = "++";
+}
+} // namespace detail
+
+inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) const {
+    std::string line;
+    std::string section = "default";
+
+    std::vector<ConfigItem> output;
+    bool defaultArray = (arrayStart == '\0' || arrayStart == ' ') && arrayStart == arrayEnd;
+    char aStart = (defaultArray) ? '[' : arrayStart;
+    char aEnd = (defaultArray) ? ']' : arrayEnd;
+    char aSep = (defaultArray && arraySeparator == ' ') ? ',' : arraySeparator;
+
+    while(getline(input, line)) {
+        std::vector<std::string> items_buffer;
+        std::string name;
+
+        detail::trim(line);
+        std::size_t len = line.length();
+        if(len > 1 && line.front() == '[' && line.back() == ']') {
+            if(section != "default") {
+                // insert a section end which is just an empty items_buffer
+                output.emplace_back();
+                output.back().parents = detail::generate_parents(section, name);
+                output.back().name = "--";
+            }
+            section = line.substr(1, len - 2);
+            // deal with double brackets for TOML
+            if(section.size() > 1 && section.front() == '[' && section.back() == ']') {
+                section = section.substr(1, section.size() - 2);
+            }
+            if(detail::to_lower(section) == "default") {
+                section = "default";
+            } else {
+                detail::checkParentSegments(output, section);
+            }
+            continue;
+        }
+        if(len == 0) {
+            continue;
+        }
+        // comment lines
+        if(line.front() == ';' || line.front() == '#' || line.front() == commentChar) {
+            continue;
+        }
+
+        // Find = in string, split and recombine
+        auto pos = line.find(valueDelimiter);
+        if(pos != std::string::npos) {
+            name = detail::trim_copy(line.substr(0, pos));
+            std::string item = detail::trim_copy(line.substr(pos + 1));
+            if(item.size() > 1 && item.front() == aStart && item.back() == aEnd) {
+                items_buffer = detail::split_up(item.substr(1, item.length() - 2), aSep);
+            } else if(defaultArray && item.find_first_of(aSep) != std::string::npos) {
+                items_buffer = detail::split_up(item, aSep);
+            } else if(defaultArray && item.find_first_of(' ') != std::string::npos) {
+                items_buffer = detail::split_up(item);
+            } else {
+                items_buffer = {item};
+            }
+        } else {
+            name = detail::trim_copy(line);
+            items_buffer = {"true"};
+        }
+        if(name.find('.') == std::string::npos) {
+            detail::remove_quotes(name);
+        }
+        // clean up quotes on the items
+        for(auto &it : items_buffer) {
+            detail::remove_quotes(it);
+        }
+
+        std::vector<std::string> parents = detail::generate_parents(section, name);
+
+        if(!output.empty() && name == output.back().name && parents == output.back().parents) {
+            output.back().inputs.insert(output.back().inputs.end(), items_buffer.begin(), items_buffer.end());
+        } else {
+            output.emplace_back();
+            output.back().parents = std::move(parents);
+            output.back().name = std::move(name);
+            output.back().inputs = std::move(items_buffer);
+        }
+    }
+    if(section != "default") {
+        // insert a section end which is just an empty items_buffer
+        std::string ename;
+        output.emplace_back();
+        output.back().parents = detail::generate_parents(section, ename);
+        output.back().name = "--";
+        while(output.back().parents.size() > 1) {
+            output.push_back(output.back());
+            output.back().parents.pop_back();
+        }
+    }
+    return output;
+}
+
 inline std::string
-ConfigINI::to_config(const App *app, bool default_also, bool write_description, std::string prefix) const {
+ConfigBase::to_config(const App *app, bool default_also, bool write_description, std::string prefix) const {
     std::stringstream out;
-    for(const Option *opt : app->get_options({})) {
-
-        // Only process option with a long-name and configurable
-        if(!opt->get_lnames().empty() && opt->get_configurable()) {
-            std::string name = prefix + opt->get_lnames()[0];
-            std::string value;
-
-            // Non-flags
-            if(opt->get_type_size() != 0) {
-
-                // If the option was found on command line
-                if(opt->count() > 0)
-                    value = detail::ini_join(opt->results());
-
-                // If the option has a default and is requested by optional argument
-                else if(default_also && !opt->get_default_str().empty())
-                    value = opt->get_default_str();
-                // Flag, one passed
-            } else if(opt->count() == 1) {
-                value = "true";
-
-                // Flag, multiple passed
-            } else if(opt->count() > 1) {
-                value = std::to_string(opt->count());
-
-                // Flag, not present
-            } else if(opt->count() == 0 && default_also) {
-                value = "false";
+    std::string commentLead;
+    commentLead.push_back(commentChar);
+    commentLead.push_back(' ');
+
+    std::vector<std::string> groups = app->get_groups();
+    bool defaultUsed = false;
+    groups.insert(groups.begin(), std::string("Options"));
+    if(write_description) {
+        out << commentLead << app->get_description() << '\n';
+    }
+    for(auto &group : groups) {
+        if(group == "Options" || group.empty()) {
+            if(defaultUsed) {
+                continue;
             }
+            defaultUsed = true;
+        }
+        if(write_description && group != "Options" && !group.empty()) {
+            out << '\n' << commentLead << group << " Options\n";
+        }
+        for(const Option *opt : app->get_options({})) {
+
+            // Only process option with a long-name and configurable
+            if(!opt->get_lnames().empty() && opt->get_configurable()) {
+                if(opt->get_group() != group) {
+                    if(!(group == "Options" && opt->get_group().empty())) {
+                        continue;
+                    }
+                }
+                std::string name = prefix + opt->get_lnames()[0];
+                std::string value = detail::ini_join(opt->reduced_results(), arraySeparator, arrayStart, arrayEnd);
 
-            if(!value.empty()) {
-                if(write_description && opt->has_description()) {
-                    if(static_cast<int>(out.tellp()) != 0) {
-                        out << std::endl;
+                if(value.empty() && default_also) {
+                    if(!opt->get_default_str().empty()) {
+                        value = detail::convert_arg_for_ini(opt->get_default_str());
+                    } else if(opt->get_expected_min() == 0) {
+                        value = "false";
                     }
-                    out << "; " << detail::fix_newlines("; ", opt->get_description()) << std::endl;
                 }
 
-                // Don't try to quote anything that is not size 1
-                if(opt->get_items_expected() != 1)
-                    out << name << "=" << value << std::endl;
-                else
-                    out << name << "=" << detail::add_quotes_if_needed(value) << std::endl;
+                if(!value.empty()) {
+                    if(write_description && opt->has_description()) {
+                        out << '\n';
+                        out << commentLead << detail::fix_newlines(commentLead, opt->get_description()) << '\n';
+                    }
+                    out << name << valueDelimiter << value << '\n';
+                }
+            }
+        }
+    }
+    auto subcommands = app->get_subcommands({});
+    for(const App *subcom : subcommands) {
+        if(subcom->get_name().empty()) {
+            if(write_description && !subcom->get_group().empty()) {
+                out << '\n' << commentLead << subcom->get_group() << " Options\n";
             }
+            out << to_config(subcom, default_also, write_description, prefix);
         }
     }
 
-    for(const App *subcom : app->get_subcommands({}))
-        out << to_config(subcom, default_also, write_description, prefix + subcom->get_name() + ".");
+    for(const App *subcom : subcommands) {
+        if(!subcom->get_name().empty()) {
+            if(subcom->get_configurable() && app->got_subcommand(subcom)) {
+                if(!prefix.empty() || app->get_parent() == nullptr) {
+                    out << '[' << prefix << subcom->get_name() << "]\n";
+                } else {
+                    std::string subname = app->get_name() + "." + subcom->get_name();
+                    auto p = app->get_parent();
+                    while(p->get_parent() != nullptr) {
+                        subname = p->get_name() + "." + subname;
+                        p = p->get_parent();
+                    }
+                    out << '[' << subname << "]\n";
+                }
+                out << to_config(subcom, default_also, write_description, "");
+            } else {
+                out << to_config(subcom, default_also, write_description, prefix + subcom->get_name() + ".");
+            }
+        }
+    }
 
     return out.str();
 }
diff --git a/packages/CLI11/include/CLI/ConfigFwd.hpp b/packages/CLI11/include/CLI/ConfigFwd.hpp
index ba009ba687a0181ace120b95e2d280e2ceb4b9f7..369458fd2184e2124e49a697b47b093b39502897 100644
--- a/packages/CLI11/include/CLI/ConfigFwd.hpp
+++ b/packages/CLI11/include/CLI/ConfigFwd.hpp
@@ -1,12 +1,16 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
 #include <algorithm>
 #include <fstream>
 #include <iostream>
 #include <string>
+#include <vector>
 
 #include "CLI/Error.hpp"
 #include "CLI/StringTools.hpp"
@@ -15,40 +19,16 @@ namespace CLI {
 
 class App;
 
-namespace detail {
-
-/// Comma separated join, adds quotes if needed
-inline std::string ini_join(std::vector<std::string> args) {
-    std::ostringstream s;
-    size_t start = 0;
-    for(const auto &arg : args) {
-        if(start++ > 0)
-            s << " ";
-
-        auto it = std::find_if(arg.begin(), arg.end(), [](char ch) { return std::isspace<char>(ch, std::locale()); });
-        if(it == arg.end())
-            s << arg;
-        else if(arg.find_first_of('\"') == std::string::npos)
-            s << '\"' << arg << '\"';
-        else
-            s << '\'' << arg << '\'';
-    }
-
-    return s.str();
-}
-
-} // namespace detail
-
 /// Holds values to load into Options
 struct ConfigItem {
     /// This is the list of parents
-    std::vector<std::string> parents;
+    std::vector<std::string> parents{};
 
     /// This is the name
-    std::string name;
+    std::string name{};
 
     /// Listing of inputs
-    std::vector<std::string> inputs;
+    std::vector<std::string> inputs{};
 
     /// The list of parents and name joined by "."
     std::string fullname() const {
@@ -61,7 +41,7 @@ struct ConfigItem {
 /// This class provides a converter for configuration files.
 class Config {
   protected:
-    std::vector<ConfigItem> items;
+    std::vector<ConfigItem> items{};
 
   public:
     /// Convert an app into a configuration
@@ -91,56 +71,61 @@ class Config {
     virtual ~Config() = default;
 };
 
-/// This converter works with INI files
-class ConfigINI : public Config {
+/// This converter works with INI/TOML files; to write proper TOML files use ConfigTOML
+class ConfigBase : public Config {
+  protected:
+    /// the character used for comments
+    char commentChar = ';';
+    /// the character used to start an array '\0' is a default to not use
+    char arrayStart = '\0';
+    /// the character used to end an array '\0' is a default to not use
+    char arrayEnd = '\0';
+    /// the character used to separate elements in an array
+    char arraySeparator = ' ';
+    /// the character used separate the name from the value
+    char valueDelimiter = '=';
+
   public:
     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;
-        std::string section = "default";
-
-        std::vector<ConfigItem> output;
-
-        while(getline(input, line)) {
-            std::vector<std::string> items_buffer;
-
-            detail::trim(line);
-            size_t len = line.length();
-            if(len > 1 && line[0] == '[' && line[len - 1] == ']') {
-                section = line.substr(1, len - 2);
-            } else if(len > 0 && line[0] != ';') {
-                output.emplace_back();
-                ConfigItem &out = output.back();
-
-                // Find = in string, split and recombine
-                auto pos = line.find('=');
-                if(pos != std::string::npos) {
-                    out.name = detail::trim_copy(line.substr(0, pos));
-                    std::string item = detail::trim_copy(line.substr(pos + 1));
-                    items_buffer = detail::split_up(item);
-                } else {
-                    out.name = detail::trim_copy(line);
-                    items_buffer = {"ON"};
-                }
-
-                if(detail::to_lower(section) != "default") {
-                    out.parents = {section};
-                }
-
-                if(out.name.find('.') != std::string::npos) {
-                    std::vector<std::string> plist = detail::split(out.name, '.');
-                    out.name = plist.back();
-                    plist.pop_back();
-                    out.parents.insert(out.parents.end(), plist.begin(), plist.end());
-                }
-
-                out.inputs.insert(std::end(out.inputs), std::begin(items_buffer), std::end(items_buffer));
-            }
-        }
-        return output;
+    std::vector<ConfigItem> from_config(std::istream &input) const override;
+    /// Specify the configuration for comment characters
+    ConfigBase *comment(char cchar) {
+        commentChar = cchar;
+        return this;
+    }
+    /// Specify the start and end characters for an array
+    ConfigBase *arrayBounds(char aStart, char aEnd) {
+        arrayStart = aStart;
+        arrayEnd = aEnd;
+        return this;
+    }
+    /// Specify the delimiter character for an array
+    ConfigBase *arrayDelimiter(char aSep) {
+        arraySeparator = aSep;
+        return this;
+    }
+    /// Specify the delimiter between a name and value
+    ConfigBase *valueSeparator(char vSep) {
+        valueDelimiter = vSep;
+        return this;
     }
 };
 
+/// the default Config is the INI file format
+using ConfigINI = ConfigBase;
+
+/// ConfigTOML generates a TOML compliant output
+class ConfigTOML : public ConfigINI {
+
+  public:
+    ConfigTOML() {
+        commentChar = '#';
+        arrayStart = '[';
+        arrayEnd = ']';
+        arraySeparator = ',';
+        valueDelimiter = '=';
+    }
+};
 } // namespace CLI
diff --git a/packages/CLI11/include/CLI/Error.hpp b/packages/CLI11/include/CLI/Error.hpp
index 115bd66d4a743a917d89deb3c48b6411b05fcac2..6d09c973ead31e67a3c04fdebe3a83a0d0ef190e 100644
--- a/packages/CLI11/include/CLI/Error.hpp
+++ b/packages/CLI11/include/CLI/Error.hpp
@@ -1,12 +1,16 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
 #include <exception>
 #include <stdexcept>
 #include <string>
 #include <utility>
+#include <vector>
 
 // CLI library includes
 #include "CLI/StringTools.hpp"
@@ -205,26 +209,29 @@ class ValidationError : public ParseError {
 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) {
+    static RequiredError Subcommand(std::size_t min_subcom) {
         if(min_subcom == 1) {
             return RequiredError("A subcommand");
         }
         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) {
+    static RequiredError
+    Option(std::size_t min_option, std::size_t max_option, std::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 + "]");
-        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);
+        }
         if((min_option == 1) && (used == 0))
             return RequiredError("At least 1 option from [" + option_list + "]");
-        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);
+        }
         if(max_option == 1)
             return RequiredError("Requires at most 1 options be given from [" + option_list + "]",
                                  ExitCodes::RequiredError);
@@ -239,15 +246,20 @@ class RequiredError : public ParseError {
 class ArgumentMismatch : public ParseError {
     CLI11_ERROR_DEF(ParseError, ArgumentMismatch)
     CLI11_ERROR_SIMPLE(ArgumentMismatch)
-    ArgumentMismatch(std::string name, int expected, size_t recieved)
+    ArgumentMismatch(std::string name, int expected, std::size_t recieved)
         : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name +
                                            ", got " + std::to_string(recieved))
                                         : ("Expected at least " + std::to_string(-expected) + " arguments to " + name +
                                            ", got " + std::to_string(recieved)),
                            ExitCodes::ArgumentMismatch) {}
 
-    static ArgumentMismatch AtLeast(std::string name, int num) {
-        return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required");
+    static ArgumentMismatch AtLeast(std::string name, int num, std::size_t received) {
+        return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required but received " +
+                                std::to_string(received));
+    }
+    static ArgumentMismatch AtMost(std::string name, int num, std::size_t received) {
+        return ArgumentMismatch(name + ": At Most " + std::to_string(num) + " required but received " +
+                                std::to_string(received));
     }
     static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) {
         return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing");
diff --git a/packages/CLI11/include/CLI/Formatter.hpp b/packages/CLI11/include/CLI/Formatter.hpp
index a5bdd709f9f6c44365f344318227af979bd04976..748fcb36f32c60ea3091ee9f7998bda974d88352 100644
--- a/packages/CLI11/include/CLI/Formatter.hpp
+++ b/packages/CLI11/include/CLI/Formatter.hpp
@@ -1,9 +1,14 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
+#include <algorithm>
 #include <string>
+#include <vector>
 
 #include "CLI/App.hpp"
 #include "CLI/FormatterFwd.hpp"
@@ -165,7 +170,9 @@ inline std::string Formatter::make_subcommands(const App *app, AppFormatMode mod
     std::vector<std::string> subcmd_groups_seen;
     for(const App *com : subcommands) {
         if(com->get_name().empty()) {
-            out << make_expanded(com);
+            if(!com->get_group().empty()) {
+                out << make_expanded(com);
+            }
             continue;
         }
         std::string group_key = com->get_group();
@@ -234,10 +241,11 @@ inline std::string Formatter::make_option_opts(const Option *opt) const {
             out << " " << get_label(opt->get_type_name());
         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)
+        if(opt->get_expected_max() == detail::expected_max_vector_size)
             out << " ...";
+        else if(opt->get_expected_min() > 1)
+            out << " x " << opt->get_expected();
+
         if(opt->get_required())
             out << " " << get_label("REQUIRED");
     }
@@ -262,11 +270,11 @@ inline std::string Formatter::make_option_usage(const Option *opt) const {
     // Note that these are positionals usages
     std::stringstream out;
     out << make_option_name(opt, true);
-
-    if(opt->get_expected() > 1)
-        out << "(" << std::to_string(opt->get_expected()) << "x)";
-    else if(opt->get_expected() < 0)
+    if(opt->get_expected_max() >= detail::expected_max_vector_size)
         out << "...";
+    else if(opt->get_expected_max() > 1)
+        out << "(" << opt->get_expected() << "x)";
+
     return opt->get_required() ? out.str() : "[" + out.str() + "]";
 }
 
diff --git a/packages/CLI11/include/CLI/FormatterFwd.hpp b/packages/CLI11/include/CLI/FormatterFwd.hpp
index 4a398245ab0aa5ebf9aeba811df4c96edcd8cdae..1dda9e1c88b6580f4c17c6776d7c9dc64f227ed3 100644
--- a/packages/CLI11/include/CLI/FormatterFwd.hpp
+++ b/packages/CLI11/include/CLI/FormatterFwd.hpp
@@ -1,11 +1,15 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
 #include <map>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "CLI/StringTools.hpp"
 
@@ -35,11 +39,11 @@ class FormatterBase {
     ///@{
 
     /// The width of the first column
-    size_t column_width_{30};
+    std::size_t column_width_{30};
 
     /// @brief The required help printout labels (user changeable)
     /// Values are Needs, Excludes, etc.
-    std::map<std::string, std::string> labels_;
+    std::map<std::string, std::string> labels_{};
 
     ///@}
     /// @name Basic
@@ -64,7 +68,7 @@ class FormatterBase {
     void label(std::string key, std::string val) { labels_[key] = val; }
 
     /// Set the column width
-    void column_width(size_t val) { column_width_ = val; }
+    void column_width(std::size_t val) { column_width_ = val; }
 
     ///@}
     /// @name Getters
@@ -79,7 +83,7 @@ class FormatterBase {
     }
 
     /// Get the current column width
-    size_t get_column_width() const { return column_width_; }
+    std::size_t get_column_width() const { return column_width_; }
 
     ///@}
 };
diff --git a/packages/CLI11/include/CLI/Macros.hpp b/packages/CLI11/include/CLI/Macros.hpp
index f7a266506d9df93cfe93a4fc3b0414fdc2589dad..44e7098a0f6e990d479a337d35bdc07085403041 100644
--- a/packages/CLI11/include/CLI/Macros.hpp
+++ b/packages/CLI11/include/CLI/Macros.hpp
@@ -1,7 +1,10 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
 // [CLI11:verbatim]
 
diff --git a/packages/CLI11/include/CLI/Option.hpp b/packages/CLI11/include/CLI/Option.hpp
index e2123abb379f714877bd3eff24e033730af4834c..c1ab02397a3f46043d6e6a9a154138ee2902d67e 100644
--- a/packages/CLI11/include/CLI/Option.hpp
+++ b/packages/CLI11/include/CLI/Option.hpp
@@ -1,7 +1,10 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
 #include <algorithm>
 #include <functional>
@@ -21,14 +24,21 @@
 namespace CLI {
 
 using results_t = std::vector<std::string>;
-using callback_t = std::function<bool(results_t)>;
+/// callback function definition
+using callback_t = std::function<bool(const results_t &)>;
 
 class Option;
 class App;
 
 using Option_p = std::unique_ptr<Option>;
-
-enum class MultiOptionPolicy : char { Throw, TakeLast, TakeFirst, Join };
+/// Enumeration of the multiOption Policy selection
+enum class MultiOptionPolicy : char {
+    Throw,     //!< Throw an error if any extra arguments were given
+    TakeLast,  //!< take only the last Expected number of arguments
+    TakeFirst, //!< take only the first Expected number of arguments
+    Join,      //!< merge all the arguments together into a single string via the delimiter character default('\n')
+    TakeAll    //!< just get all the passed argument regardless
+};
 
 /// This is the CRTP base class for Option and OptionDefaults. It was designed this way
 /// to share parts of the class; an OptionDefaults can copy to an Option.
@@ -60,7 +70,7 @@ template <typename CRTP> class OptionBase {
     /// Automatically capture default value
     bool always_capture_default_{false};
 
-    /// Policy for multiple arguments when `expected_ == 1`  (can be set on bool flags, too)
+    /// Policy for handling multiple arguments beyond the expected Max
     MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw};
 
     /// Copy the contents to another similar class (one based on OptionBase)
@@ -80,7 +90,7 @@ template <typename CRTP> class OptionBase {
     // setters
 
     /// Changes the group membership
-    CRTP *group(std::string name) {
+    CRTP *group(const std::string &name) {
         group_ = name;
         return static_cast<CRTP *>(this);
     }
@@ -119,7 +129,7 @@ template <typename CRTP> class OptionBase {
     /// The status of configurable
     bool get_disable_flag_override() const { return disable_flag_override_; }
 
-    /// Get the current delimeter char
+    /// Get the current delimiter char
     char get_delimiter() const { return delimiter_; }
 
     /// Return true if this will automatically capture the default value for help printing
@@ -144,13 +154,28 @@ template <typename CRTP> class OptionBase {
         return self;
     }
 
-    /// Set the multi option policy to take last
+    /// Set the multi option policy to take all arguments
+    CRTP *take_all() {
+        auto self = static_cast<CRTP *>(this);
+        self->multi_option_policy(MultiOptionPolicy::TakeAll);
+        return self;
+    }
+
+    /// Set the multi option policy to join
     CRTP *join() {
         auto self = static_cast<CRTP *>(this);
         self->multi_option_policy(MultiOptionPolicy::Join);
         return self;
     }
 
+    /// Set the multi option policy to join with a specific delimiter
+    CRTP *join(char delim) {
+        auto self = static_cast<CRTP *>(this);
+        self->delimiter_ = delim;
+        self->multi_option_policy(MultiOptionPolicy::Join);
+        return self;
+    }
+
     /// Allow in a configuration file
     CRTP *configurable(bool value = true) {
         configurable_ = value;
@@ -211,33 +236,33 @@ class Option : public OptionBase<Option> {
     ///@{
 
     /// A list of the short names (`-a`) without the leading dashes
-    std::vector<std::string> snames_;
+    std::vector<std::string> snames_{};
 
-    /// A list of the long names (`--a`) without the leading dashes
-    std::vector<std::string> lnames_;
+    /// A list of the long names (`--long`) without the leading dashes
+    std::vector<std::string> lnames_{};
 
     /// A list of the flag names with the appropriate default value, the first part of the pair should be duplicates of
     /// what is in snames or lnames but will trigger a particular response on a flag
-    std::vector<std::pair<std::string, std::string>> default_flag_values_;
+    std::vector<std::pair<std::string, std::string>> default_flag_values_{};
 
     /// a list of flag names with specified default values;
-    std::vector<std::string> fnames_;
+    std::vector<std::string> fnames_{};
 
     /// A positional name
-    std::string pname_;
+    std::string pname_{};
 
     /// If given, check the environment for this option
-    std::string envname_;
+    std::string envname_{};
 
     ///@}
     /// @name Help
     ///@{
 
     /// The description for help strings
-    std::string description_;
+    std::string description_{};
 
     /// A human readable default value, either manually set, captured, or captured by default
-    std::string default_str_;
+    std::string default_str_{};
 
     /// A human readable type value, set when App creates this
     ///
@@ -245,56 +270,69 @@ class Option : public OptionBase<Option> {
     std::function<std::string()> type_name_{[]() { return std::string(); }};
 
     /// Run this function to capture a default (ignore if empty)
-    std::function<std::string()> default_function_;
+    std::function<std::string()> default_function_{};
 
     ///@}
     /// @name Configuration
     ///@{
 
-    /// The number of arguments that make up one option. -1=unlimited (vector-like), 0=flag, 1=normal option,
-    /// 2=complex/pair, etc. Set only when the option is created; this is intrinsic to the type. Eventually, -2 may mean
-    /// vector of pairs.
-    int type_size_{1};
+    /// The number of arguments that make up one option. max is the nominal type size, min is the minimum number of
+    /// strings
+    int type_size_max_{1};
+    /// The minimum number of arguments an option should be expecting
+    int type_size_min_{1};
 
-    /// The number of expected values, type_size_ must be < 0. Ignored for flag. N < 0 means at least -N values.
-    int expected_{1};
+    /// The minimum number of expected values
+    int expected_min_{1};
+    /// The maximum number of expected values
+    int expected_max_{1};
 
-    /// A list of validators to run on each value parsed
-    std::vector<Validator> validators_;
+    /// A list of Validators to run on each value parsed
+    std::vector<Validator> validators_{};
 
     /// A list of options that are required with this option
-    std::set<Option *> needs_;
+    std::set<Option *> needs_{};
 
     /// A list of options that are excluded with this option
-    std::set<Option *> excludes_;
+    std::set<Option *> excludes_{};
 
     ///@}
     /// @name Other
     ///@{
 
     /// Remember the parent app
-    App *parent_;
+    App *parent_{nullptr};
 
     /// Options store a callback to do all the work
-    callback_t callback_;
+    callback_t callback_{};
 
     ///@}
     /// @name Parsing results
     ///@{
 
-    /// Results of parsing
-    results_t results_;
-
+    /// complete Results of parsing
+    results_t results_{};
+    /// results after reduction
+    results_t proc_results_{};
+    /// enumeration for the option state machine
+    enum class option_state {
+        parsing = 0,      //!< The option is currently collecting parsed results
+        validated = 2,    //!< the results have been validated
+        reduced = 4,      //!< a subset of results has been generated
+        callback_run = 6, //!< the callback has been executed
+    };
     /// Whether the callback has run (needed for INI parsing)
-    bool callback_run_{false};
-
+    option_state current_option_state_{option_state::parsing};
+    /// Specify that extra args beyond type_size_max should be allowed
+    bool allow_extra_args_{false};
+    /// Specify that the option should act like a flag vs regular option
+    bool flag_like_{false};
+    /// Control option to run the callback to set the default
+    bool run_callback_for_default_{false};
     ///@}
 
     /// Making an option by hand is not defined, it must be made by the App class
-    Option(std::string option_name,
-           std::string option_description,
-           std::function<bool(results_t)> callback,
-           App *parent)
+    Option(std::string option_name, std::string option_description, callback_t callback, App *parent)
         : description_(std::move(option_description)), parent_(parent), callback_(std::move(callback)) {
         std::tie(snames_, lnames_, pname_) = detail::get_names(detail::split_names(option_name));
     }
@@ -303,51 +341,89 @@ class Option : public OptionBase<Option> {
     /// @name Basic
     ///@{
 
+    Option(const Option &) = delete;
+    Option &operator=(const Option &) = delete;
+
     /// Count the total number of times an option was passed
-    size_t count() const { return results_.size(); }
+    std::size_t count() const { return results_.size(); }
 
     /// True if the option was not passed
     bool empty() const { return results_.empty(); }
 
     /// This class is true if option is passed.
-    operator bool() const { return !empty(); }
+    explicit operator bool() const { return !empty(); }
 
     /// Clear the parsed results (mostly for testing)
-    void clear() { results_.clear(); }
+    void clear() {
+        results_.clear();
+        current_option_state_ = option_state::parsing;
+    }
 
     ///@}
     /// @name Setting options
     ///@{
 
-    /// Set the number of expected arguments (Flags don't use this)
+    /// Set the number of expected arguments
     Option *expected(int value) {
+        if(value < 0) {
+            expected_min_ = -value;
+            if(expected_max_ < expected_min_) {
+                expected_max_ = expected_min_;
+            }
+            allow_extra_args_ = true;
+            flag_like_ = false;
+        } else if(value == detail::expected_max_vector_size) {
+            expected_min_ = 1;
+            expected_max_ = detail::expected_max_vector_size;
+            allow_extra_args_ = true;
+            flag_like_ = false;
+        } else {
+            expected_min_ = value;
+            expected_max_ = value;
+            flag_like_ = (expected_min_ == 0);
+        }
+        return this;
+    }
 
-        // Break if this is a flag
-        if(type_size_ == 0)
-            throw IncorrectConstruction::SetFlag(get_name(true, true));
-
-        // Setting 0 is not allowed
-        if(value == 0)
-            throw IncorrectConstruction::Set0Opt(get_name());
-
-        // No change is okay, quit now
-        if(expected_ == value)
-            return this;
+    /// Set the range of expected arguments
+    Option *expected(int value_min, int value_max) {
+        if(value_min < 0) {
+            value_min = -value_min;
+        }
 
-        // Type must be a vector
-        if(type_size_ >= 0)
-            throw IncorrectConstruction::ChangeNotVector(get_name());
+        if(value_max < 0) {
+            value_max = detail::expected_max_vector_size;
+        }
+        if(value_max < value_min) {
+            expected_min_ = value_max;
+            expected_max_ = value_min;
+        } else {
+            expected_max_ = value_max;
+            expected_min_ = value_min;
+        }
 
-        // TODO: Can support multioption for non-1 values (except for join)
-        if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw)
-            throw IncorrectConstruction::AfterMultiOpt(get_name());
+        return this;
+    }
+    /// Set the value of allow_extra_args which allows extra value arguments on the flag or option to be included
+    /// with each instance
+    Option *allow_extra_args(bool value = true) {
+        allow_extra_args_ = value;
+        return this;
+    }
+    /// Get the current value of allow extra args
+    bool get_allow_extra_args() const { return allow_extra_args_; }
 
-        expected_ = value;
+    /// Set the value of run_callback_for_default which controls whether the callback function should be called to set
+    /// the default This is controlled automatically but could be manipulated by the user.
+    Option *run_callback_for_default(bool value = true) {
+        run_callback_for_default_ = value;
         return this;
     }
+    /// Get the current value of run_callback_for_default
+    bool get_run_callback_for_default() const { return run_callback_for_default_; }
 
     /// Adds a Validator with a built in type name
-    Option *check(Validator validator, std::string validator_name = "") {
+    Option *check(Validator validator, const std::string &validator_name = "") {
         validator.non_modifying();
         validators_.push_back(std::move(validator));
         if(!validator_name.empty())
@@ -356,24 +432,24 @@ class Option : public OptionBase<Option> {
     }
 
     /// Adds a Validator. Takes a const string& and returns an error message (empty if conversion/check is okay).
-    Option *check(std::function<std::string(const std::string &)> validator,
-                  std::string validator_description = "",
-                  std::string validator_name = "") {
-        validators_.emplace_back(validator, std::move(validator_description), std::move(validator_name));
+    Option *check(std::function<std::string(const std::string &)> Validator,
+                  std::string Validator_description = "",
+                  std::string Validator_name = "") {
+        validators_.emplace_back(Validator, std::move(Validator_description), std::move(Validator_name));
         validators_.back().non_modifying();
         return this;
     }
 
-    /// Adds a transforming validator with a built in type name
-    Option *transform(Validator validator, std::string validator_name = "") {
-        validators_.insert(validators_.begin(), std::move(validator));
-        if(!validator_name.empty())
-            validators_.front().name(validator_name);
+    /// Adds a transforming Validator with a built in type name
+    Option *transform(Validator Validator, const std::string &Validator_name = "") {
+        validators_.insert(validators_.begin(), std::move(Validator));
+        if(!Validator_name.empty())
+            validators_.front().name(Validator_name);
         return this;
     }
 
-    /// Adds a validator-like function that can change result
-    Option *transform(std::function<std::string(std::string)> func,
+    /// Adds a Validator-like function that can change result
+    Option *transform(const std::function<std::string(std::string)> &func,
                       std::string transform_description = "",
                       std::string transform_name = "") {
         validators_.insert(validators_.begin(),
@@ -389,7 +465,7 @@ class Option : public OptionBase<Option> {
     }
 
     /// Adds a user supplied function to run on each item passed in (communicate though lambda capture)
-    Option *each(std::function<void(std::string)> func) {
+    Option *each(const std::function<void(std::string)> &func) {
         validators_.emplace_back(
             [func](std::string &inout) {
                 func(inout);
@@ -399,22 +475,23 @@ class Option : public OptionBase<Option> {
         return this;
     }
     /// Get a named Validator
-    Validator *get_validator(const std::string &validator_name = "") {
-        for(auto &validator : validators_) {
-            if(validator_name == validator.get_name()) {
-                return &validator;
+    Validator *get_validator(const std::string &Validator_name = "") {
+        for(auto &Validator : validators_) {
+            if(Validator_name == Validator.get_name()) {
+                return &Validator;
             }
         }
-        if((validator_name.empty()) && (!validators_.empty())) {
+        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) {
+        // This is an signed int so that it is not equivalent to a pointer.
         if(index >= 0 && index < static_cast<int>(validators_.size())) {
-            return &(validators_[index]);
+            return &(validators_[static_cast<decltype(validators_)::size_type>(index)]);
         }
         throw OptionNotFound("Validator index is not valid");
     }
@@ -497,7 +574,7 @@ class Option : public OptionBase<Option> {
 
     /// Sets environment variable to read if no option given
     Option *envname(std::string name) {
-        envname_ = name;
+        envname_ = std::move(name);
         return this;
     }
 
@@ -552,10 +629,15 @@ class Option : public OptionBase<Option> {
 
     /// Take the last argument if given multiple times (or another policy)
     Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
-
-        if(get_items_expected() < 0)
-            throw IncorrectConstruction::MultiOptionPolicy(get_name());
-        multi_option_policy_ = value;
+        if(value != multi_option_policy_) {
+            if(multi_option_policy_ == MultiOptionPolicy::Throw && expected_max_ == detail::expected_max_vector_size &&
+               expected_min_ > 1) { // this bizarre condition is to maintain backwards compatibility
+                                    // with the previous behavior of expected_ with vectors
+                expected_max_ = expected_min_;
+            }
+            multi_option_policy_ = value;
+            current_option_state_ = option_state::parsing;
+        }
         return this;
     }
 
@@ -569,7 +651,12 @@ class Option : public OptionBase<Option> {
     ///@{
 
     /// The number of arguments the option expects
-    int get_type_size() const { return type_size_; }
+    int get_type_size() const { return type_size_min_; }
+
+    /// The minimum number of arguments the option expects
+    int get_type_size_min() const { return type_size_min_; }
+    /// The maximum number of arguments the option expects
+    int get_type_size_max() const { return type_size_max_; }
 
     /// The environment variable associated to this value
     std::string get_envname() const { return envname_; }
@@ -580,10 +667,6 @@ 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_default_str() const { return default_str_; }
 
@@ -591,37 +674,32 @@ class Option : public OptionBase<Option> {
     callback_t get_callback() const { return callback_; }
 
     /// Get the long names
-    const std::vector<std::string> get_lnames() const { return lnames_; }
+    const std::vector<std::string> &get_lnames() const { return lnames_; }
 
     /// Get the short names
-    const std::vector<std::string> get_snames() const { return snames_; }
+    const std::vector<std::string> &get_snames() const { return snames_; }
 
     /// Get the flag names with specified default values
-    const std::vector<std::string> get_fnames() const { return fnames_; }
+    const std::vector<std::string> &get_fnames() const { return fnames_; }
 
     /// The number of times the option expects to be included
-    int get_expected() const { return expected_; }
+    int get_expected() const { return expected_min_; }
 
-    /// \brief The total number of expected values (including the type)
-    /// This is positive if exactly this number is expected, and negative for at least N values
-    ///
-    /// v = fabs(size_type*expected)
-    /// !MultiOptionPolicy::Throw
-    ///           | Expected < 0  | Expected == 0 | Expected > 0
-    /// Size < 0  |      -v       |       0       |     -v
-    /// Size == 0 |       0       |       0       |      0
-    /// Size > 0  |      -v       |       0       |     -v       // Expected must be 1
-    ///
-    /// MultiOptionPolicy::Throw
-    ///           | Expected < 0  | Expected == 0 | Expected > 0
-    /// Size < 0  |      -v       |       0       |      v
-    /// Size == 0 |       0       |       0       |      0
-    /// Size > 0  |       v       |       0       |      v      // Expected must be 1
-    ///
-    int get_items_expected() const {
-        return std::abs(type_size_ * expected_) *
-               ((multi_option_policy_ != MultiOptionPolicy::Throw || (expected_ < 0 && type_size_ < 0) ? -1 : 1));
+    /// The number of times the option expects to be included
+    int get_expected_min() const { return expected_min_; }
+    /// The max number of times the option expects to be included
+    int get_expected_max() const { return expected_max_; }
+
+    /// The total min number of expected  string values to be used
+    int get_items_expected_min() const { return type_size_min_ * expected_min_; }
+
+    /// Get the maximum number of items expected to be returned and used for the callback
+    int get_items_expected_max() const {
+        int t = type_size_max_;
+        return detail::checked_multiply(t, expected_max_) ? t : detail::expected_max_vector_size;
     }
+    /// The total min number of expected  string values to be used
+    int get_items_expected() const { return get_items_expected_min(); }
 
     /// True if the argument can be given directly
     bool get_positional() const { return pname_.length() > 0; }
@@ -711,66 +789,26 @@ class Option : public OptionBase<Option> {
     /// Process the callback
     void run_callback() {
 
-        callback_run_ = true;
-
-        // 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, index);
-                ++index;
-                if(!err_msg.empty())
-                    throw ValidationError(get_name(), err_msg);
-            }
+        if(current_option_state_ == option_state::parsing) {
+            _validate_results(results_);
+            current_option_state_ = option_state::validated;
         }
-        if(!(callback_)) {
-            return;
-        }
-        bool local_result;
-
-        // Num items expected or length of vector, always at least 1
-        // Only valid for a trimming policy
-        int trim_size =
-            std::min<int>(std::max<int>(std::abs(get_items_expected()), 1), static_cast<int>(results_.size()));
-
-        // Operation depends on the policy setting
-        if(multi_option_policy_ == MultiOptionPolicy::TakeLast) {
-            // Allow multi-option sizes (including 0)
-            results_t partial_result{results_.end() - trim_size, results_.end()};
-            local_result = !callback_(partial_result);
-
-        } else if(multi_option_policy_ == MultiOptionPolicy::TakeFirst) {
-            results_t partial_result{results_.begin(), results_.begin() + trim_size};
-            local_result = !callback_(partial_result);
-
-        } else if(multi_option_policy_ == MultiOptionPolicy::Join) {
-            results_t partial_result = {detail::join(results_, "\n")};
-            local_result = !callback_(partial_result);
 
-        } else {
-            // Exact number required
-            if(get_items_expected() > 0) {
-                if(results_.size() != static_cast<size_t>(get_items_expected()))
-                    throw ArgumentMismatch(get_name(), get_items_expected(), results_.size());
-                // Variable length list
-            } 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())) != 0u)
-                    throw ArgumentMismatch(get_name(), get_items_expected(), results_.size());
-            }
-            local_result = !callback_(results_);
+        if(current_option_state_ < option_state::reduced) {
+            _reduce_results(proc_results_, results_);
+            current_option_state_ = option_state::reduced;
         }
+        if(current_option_state_ >= option_state::reduced) {
+            current_option_state_ = option_state::callback_run;
+            if(!(callback_)) {
+                return;
+            }
+            const results_t &send_results = proc_results_.empty() ? results_ : proc_results_;
+            bool local_result = callback_(send_results);
 
-        if(local_result)
-            throw ConversionError(get_name(), results_);
+            if(!local_result)
+                throw ConversionError(get_name(), results_);
+        }
     }
 
     /// If options share any of the same names, find it
@@ -818,11 +856,13 @@ class Option : public OptionBase<Option> {
     }
 
     /// Requires "-" to be removed from string
-    bool check_sname(std::string name) const { return (detail::find_member(name, snames_, ignore_case_) >= 0); }
+    bool check_sname(std::string name) const {
+        return (detail::find_member(std::move(name), snames_, ignore_case_) >= 0);
+    }
 
     /// Requires "--" to be removed from string
     bool check_lname(std::string name) const {
-        return (detail::find_member(name, lnames_, ignore_case_, ignore_underscore_) >= 0);
+        return (detail::find_member(std::move(name), lnames_, ignore_case_, ignore_underscore_) >= 0);
     }
 
     /// Requires "--" to be removed from string
@@ -830,11 +870,12 @@ class Option : public OptionBase<Option> {
         if(fnames_.empty()) {
             return false;
         }
-        return (detail::find_member(name, fnames_, ignore_case_, ignore_underscore_) >= 0);
+        return (detail::find_member(std::move(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 {
+    /// 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(const std::string &name, std::string input_value) const {
         static const std::string trueString{"true"};
         static const std::string falseString{"false"};
         static const std::string emptyString{"{}"};
@@ -843,8 +884,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) {
-                    // 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) {
+                    // We can static cast this to std::size_t because it is more than 0 in this block
+                    if(default_flag_values_[static_cast<std::size_t>(default_ind)].second != input_value) {
                         throw(ArgumentMismatch::FlagOverride(name));
                     }
                 } else {
@@ -856,12 +897,16 @@ 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_[static_cast<size_t>(ind)].second;
+            if(flag_like_) {
+                return (ind < 0) ? trueString : default_flag_values_[static_cast<std::size_t>(ind)].second;
+            } else {
+                return (ind < 0) ? default_str_ : default_flag_values_[static_cast<std::size_t>(ind)].second;
+            }
         }
         if(ind < 0) {
             return input_value;
         }
-        if(default_flag_values_[static_cast<size_t>(ind)].second == falseString) {
+        if(default_flag_values_[static_cast<std::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));
@@ -875,73 +920,80 @@ class Option : public OptionBase<Option> {
 
     /// Puts a result at the end
     Option *add_result(std::string s) {
-        _add_result(std::move(s));
-        callback_run_ = false;
+        _add_result(std::move(s), results_);
+        current_option_state_ = option_state::parsing;
         return this;
     }
 
     /// Puts a result at the end and get a count of the number of arguments actually added
     Option *add_result(std::string s, int &results_added) {
-        results_added = _add_result(std::move(s));
-        callback_run_ = false;
+        results_added = _add_result(std::move(s), results_);
+        current_option_state_ = option_state::parsing;
         return this;
     }
 
     /// Puts a result at the end
     Option *add_result(std::vector<std::string> s) {
         for(auto &str : s) {
-            _add_result(std::move(str));
+            _add_result(std::move(str), results_);
         }
-        callback_run_ = false;
+        current_option_state_ = option_state::parsing;
         return this;
     }
 
     /// Get a copy of the results
-    std::vector<std::string> results() const { return results_; }
+    results_t results() const { return results_; }
+
+    /// Get a copy of the results
+    results_t reduced_results() const {
+        results_t res = proc_results_.empty() ? results_ : proc_results_;
+        if(current_option_state_ < option_state::reduced) {
+            if(current_option_state_ == option_state::parsing) {
+                res = results_;
+                _validate_results(res);
+            }
+            if(!res.empty()) {
+                results_t extra;
+                _reduce_results(extra, res);
+                if(!extra.empty()) {
+                    res = std::move(extra);
+                }
+            }
+        }
+        return res;
+    }
 
     /// 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>
+    template <typename T, enable_if_t<!std::is_const<T>::value, detail::enabler> = detail::dummy>
     void results(T &output) const {
         bool retval;
-        if(results_.empty()) {
-            retval = detail::lexical_cast(default_str_, output);
-        } else if(results_.size() == 1) {
-            retval = detail::lexical_cast(results_[0], output);
+        if(current_option_state_ >= option_state::reduced || (results_.size() == 1 && validators_.empty())) {
+            const results_t &res = (proc_results_.empty()) ? results_ : proc_results_;
+            retval = detail::lexical_conversion<T, T>(res, output);
         } else {
-            switch(multi_option_policy_) {
-            case MultiOptionPolicy::TakeFirst:
-                retval = detail::lexical_cast(results_.front(), output);
-                break;
-            case MultiOptionPolicy::TakeLast:
-            default:
-                retval = detail::lexical_cast(results_.back(), output);
-                break;
-            case MultiOptionPolicy::Throw:
-                throw ConversionError(get_name(), results_);
-            case MultiOptionPolicy::Join:
-                retval = detail::lexical_cast(detail::join(results_), output);
-                break;
+            results_t res;
+            if(results_.empty()) {
+                if(!default_str_.empty()) {
+                    //_add_results takes an rvalue only
+                    _add_result(std::string(default_str_), res);
+                    _validate_results(res);
+                    results_t extra;
+                    _reduce_results(extra, res);
+                    if(!extra.empty()) {
+                        res = std::move(extra);
+                    }
+                } else {
+                    res.emplace_back();
+                }
+            } else {
+                res = reduced_results();
             }
+            retval = detail::lexical_conversion<T, T>(res, output);
         }
         if(!retval) {
             throw ConversionError(get_name(), results_);
         }
     }
-    /// 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;
-
-        for(const auto &elem : results_) {
-            output.emplace_back();
-            retval &= detail::lexical_cast(elem, output.back());
-        }
-
-        if(!retval) {
-            throw ConversionError(get_name(), results_);
-        }
-    }
 
     /// Return the results as the specified type
     template <typename T> T as() const {
@@ -951,7 +1003,7 @@ class Option : public OptionBase<Option> {
     }
 
     /// See if the callback has been run already
-    bool get_callback_run() const { return callback_run_; }
+    bool get_callback_run() const { return (current_option_state_ == option_state::callback_run); }
 
     ///@}
     /// @name Custom options
@@ -959,7 +1011,7 @@ class Option : public OptionBase<Option> {
 
     /// Set the type function to run when displayed on this option
     Option *type_name_fn(std::function<std::string()> typefun) {
-        type_name_ = typefun;
+        type_name_ = std::move(typefun);
         return this;
     }
 
@@ -971,11 +1023,40 @@ class Option : public OptionBase<Option> {
 
     /// Set a custom option size
     Option *type_size(int option_type_size) {
-        type_size_ = option_type_size;
-        if(type_size_ == 0)
+        if(option_type_size < 0) {
+            // this section is included for backwards compatibility
+            type_size_max_ = -option_type_size;
+            type_size_min_ = -option_type_size;
+            expected_max_ = detail::expected_max_vector_size;
+        } else {
+            type_size_max_ = option_type_size;
+            if(type_size_max_ < detail::expected_max_vector_size) {
+                type_size_min_ = option_type_size;
+            }
+            if(type_size_max_ == 0)
+                required_ = false;
+        }
+        return this;
+    }
+    /// Set a custom option type size range
+    Option *type_size(int option_type_size_min, int option_type_size_max) {
+        if(option_type_size_min < 0 || option_type_size_max < 0) {
+            // this section is included for backwards compatibility
+            expected_max_ = detail::expected_max_vector_size;
+            option_type_size_min = (std::abs)(option_type_size_min);
+            option_type_size_max = (std::abs)(option_type_size_max);
+        }
+
+        if(option_type_size_min > option_type_size_max) {
+            type_size_max_ = option_type_size_min;
+            type_size_min_ = option_type_size_max;
+        } else {
+            type_size_min_ = option_type_size_min;
+            type_size_max_ = option_type_size_max;
+        }
+        if(type_size_max_ == 0) {
             required_ = false;
-        if(option_type_size < 0)
-            expected_ = -1;
+        }
         return this;
     }
 
@@ -995,17 +1076,34 @@ class Option : public OptionBase<Option> {
 
     /// Set the default value string representation (does not change the contained value)
     Option *default_str(std::string val) {
-        default_str_ = val;
+        default_str_ = std::move(val);
         return this;
     }
 
-    /// 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_;
-        results_ = {val};
-        run_callback();
+    /// Set the default value and validate the results and run the callback if appropriate to set the value into the
+    /// bound value only available for types that can be converted to a string
+    template <typename X> Option *default_val(const X &val) {
+        std::string val_str = detail::to_string(val);
+        auto old_option_state = current_option_state_;
+        results_t old_results{std::move(results_)};
+        results_.clear();
+        try {
+            add_result(val_str);
+            if(run_callback_for_default_) {
+                run_callback(); // run callback sets the state we need to reset it again
+                current_option_state_ = option_state::parsing;
+            } else {
+                _validate_results(results_);
+                current_option_state_ = old_option_state;
+            }
+        } catch(const CLI::Error &) {
+            // this should be done
+            results_ = std::move(old_results);
+            current_option_state_ = old_option_state;
+            throw;
+        }
         results_ = std::move(old_results);
+        default_str_ = std::move(val_str);
         return this;
     }
 
@@ -1013,8 +1111,8 @@ class Option : public OptionBase<Option> {
     std::string get_type_name() const {
         std::string full_type_name = type_name_();
         if(!validators_.empty()) {
-            for(auto &validator : validators_) {
-                std::string vtype = validator.get_description();
+            for(auto &Validator : validators_) {
+                std::string vtype = Validator.get_description();
                 if(!vtype.empty()) {
                     full_type_name += ":" + vtype;
                 }
@@ -1024,9 +1122,106 @@ class Option : public OptionBase<Option> {
     }
 
   private:
-    // Run a result through the validators
-    std::string _validate(std::string &result, int index) {
+    /// Run the results through the Validators
+    void _validate_results(results_t &res) const {
+        // Run the Validators (can change the string)
+        if(!validators_.empty()) {
+            if(type_size_max_ > 1) { // in this context index refers to the index in the type
+                int index = 0;
+                if(get_items_expected_max() < static_cast<int>(res.size()) &&
+                   multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) {
+                    // create a negative index for the earliest ones
+                    index = get_items_expected_max() - static_cast<int>(res.size());
+                }
+
+                for(std::string &result : res) {
+                    if(result.empty() && type_size_max_ != type_size_min_ && index >= 0) {
+                        index = 0; // reset index for variable size chunks
+                        continue;
+                    }
+                    auto err_msg = _validate(result, (index >= 0) ? (index % type_size_max_) : index);
+                    if(!err_msg.empty())
+                        throw ValidationError(get_name(), err_msg);
+                    ++index;
+                }
+            } else {
+                int index = 0;
+                if(expected_max_ < static_cast<int>(res.size()) &&
+                   multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) {
+                    // create a negative index for the earliest ones
+                    index = expected_max_ - static_cast<int>(res.size());
+                }
+                for(std::string &result : res) {
+                    auto err_msg = _validate(result, index);
+                    ++index;
+                    if(!err_msg.empty())
+                        throw ValidationError(get_name(), err_msg);
+                }
+            }
+        }
+    }
+
+    /** reduce the results in accordance with the MultiOptionPolicy
+    @param[out] res results are assigned to res if there if they are different
+    */
+    void _reduce_results(results_t &res, const results_t &original) const {
+
+        // max num items expected or length of vector, always at least 1
+        // Only valid for a trimming policy
+
+        res.clear();
+        // Operation depends on the policy setting
+        switch(multi_option_policy_) {
+        case MultiOptionPolicy::TakeAll:
+            break;
+        case MultiOptionPolicy::TakeLast: {
+            // Allow multi-option sizes (including 0)
+            std::size_t trim_size = std::min<std::size_t>(
+                static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)), original.size());
+            if(original.size() != trim_size) {
+                res.assign(original.end() - static_cast<results_t::difference_type>(trim_size), original.end());
+            }
+        } break;
+        case MultiOptionPolicy::TakeFirst: {
+            std::size_t trim_size = std::min<std::size_t>(
+                static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)), original.size());
+            if(original.size() != trim_size) {
+                res.assign(original.begin(), original.begin() + static_cast<results_t::difference_type>(trim_size));
+            }
+        } break;
+        case MultiOptionPolicy::Join:
+            if(results_.size() > 1) {
+                res.push_back(detail::join(original, std::string(1, (delimiter_ == '\0') ? '\n' : delimiter_)));
+            }
+            break;
+        case MultiOptionPolicy::Throw:
+        default: {
+            auto num_min = static_cast<std::size_t>(get_items_expected_min());
+            auto num_max = static_cast<std::size_t>(get_items_expected_max());
+            if(num_min == 0) {
+                num_min = 1;
+            }
+            if(num_max == 0) {
+                num_max = 1;
+            }
+            if(original.size() < num_min) {
+                throw ArgumentMismatch::AtLeast(get_name(), static_cast<int>(num_min), original.size());
+            }
+            if(original.size() > num_max) {
+                throw ArgumentMismatch::AtMost(get_name(), static_cast<int>(num_max), original.size());
+            }
+            break;
+        }
+        }
+    }
+
+    // Run a result through the Validators
+    std::string _validate(std::string &result, int index) const {
         std::string err_msg;
+        if(result.empty() && expected_min_ == 0) {
+            // an empty with nothing expected is allowed
+            return err_msg;
+        }
         for(const auto &vali : validators_) {
             auto v = vali.get_application_index();
             if(v == -1 || v == index) {
@@ -1039,25 +1234,37 @@ class Option : public OptionBase<Option> {
                     break;
             }
         }
+
         return err_msg;
     }
 
     /// Add a single result to the result set, taking into account delimiters
-    int _add_result(std::string &&result) {
+    int _add_result(std::string &&result, std::vector<std::string> &res) const {
         int result_count = 0;
+        if(allow_extra_args_ && !result.empty() && result.front() == '[' &&
+           result.back() == ']') { // this is now a vector string likely from the default or user entry
+            result.pop_back();
+
+            for(auto &var : CLI::detail::split(result.substr(1), ',')) {
+                if(!var.empty()) {
+                    result_count += _add_result(std::move(var), res);
+                }
+            }
+            return result_count;
+        }
         if(delimiter_ == '\0') {
-            results_.push_back(std::move(result));
+            res.push_back(std::move(result));
             ++result_count;
         } else {
             if((result.find_first_of(delimiter_) != std::string::npos)) {
                 for(const auto &var : CLI::detail::split(result, delimiter_)) {
                     if(!var.empty()) {
-                        results_.push_back(var);
+                        res.push_back(var);
                         ++result_count;
                     }
                 }
             } else {
-                results_.push_back(std::move(result));
+                res.push_back(std::move(result));
                 ++result_count;
             }
         }
diff --git a/packages/CLI11/include/CLI/Split.hpp b/packages/CLI11/include/CLI/Split.hpp
index 020c590bdb69afa0f9ac1de9b20dbe7f44332652..3994c277f23b0faace7692cbfa5f8194d9f9d892 100644
--- a/packages/CLI11/include/CLI/Split.hpp
+++ b/packages/CLI11/include/CLI/Split.hpp
@@ -1,10 +1,14 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
 #include <string>
 #include <tuple>
+#include <utility>
 #include <vector>
 
 #include "CLI/Error.hpp"
@@ -58,7 +62,7 @@ inline bool split_windows_style(const std::string &current, std::string &name, s
 // Splits a string into multiple long and short names
 inline std::vector<std::string> split_names(std::string current) {
     std::vector<std::string> output;
-    size_t val;
+    std::size_t val;
     while((val = current.find(",")) != std::string::npos) {
         output.push_back(trim_copy(current.substr(0, val)));
         current = current.substr(val + 1);
diff --git a/packages/CLI11/include/CLI/StringTools.hpp b/packages/CLI11/include/CLI/StringTools.hpp
index 4ed8ea057fa908cbd2ff9caaf55bed8348a14a64..903af1087ce3588c7ce5e157d937b1fb46f33f65 100644
--- a/packages/CLI11/include/CLI/StringTools.hpp
+++ b/packages/CLI11/include/CLI/StringTools.hpp
@@ -1,7 +1,10 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
 #include <algorithm>
 #include <iomanip>
@@ -28,10 +31,12 @@ std::ostream &operator<<(std::ostream &in, const T &item) {
 } // namespace enums
 
 /// Export to CLI namespace
-using namespace enums;
+using enums::operator<<;
 
 namespace detail {
-
+/// a constant defining an expected max vector size defined to be a big number that could be multiplied by 4 and not
+/// produce overflow for some expected uses
+constexpr int expected_max_vector_size{1 << 29};
 // Based on http://stackoverflow.com/questions/236129/split-a-string-in-c
 /// Split a string by a delim
 inline std::vector<std::string> split(const std::string &s, char delim) {
@@ -82,7 +87,7 @@ std::string join(const T &v, Callable func, std::string delim = ",") {
 /// Join a string in reverse order
 template <typename T> std::string rjoin(const T &v, std::string delim = ",") {
     std::ostringstream s;
-    for(size_t start = 0; start < v.size(); start++) {
+    for(std::size_t start = 0; start < v.size(); start++) {
         if(start > 0)
             s << delim;
         s << v[v.size() - start - 1];
@@ -133,13 +138,24 @@ inline std::string trim_copy(const std::string &str) {
     return trim(s);
 }
 
+/// remove quotes at the front and back of a string either '"' or '\''
+inline std::string &remove_quotes(std::string &str) {
+    if(str.length() > 1 && (str.front() == '"' || str.front() == '\'')) {
+        if(str.front() == str.back()) {
+            str.pop_back();
+            str.erase(str.begin(), str.begin() + 1);
+        }
+    }
+    return str;
+}
+
 /// Make a copy of the string and then trim it, any filter string can be used (any char in string is filtered)
 inline std::string trim_copy(const std::string &str, const std::string &filter) {
     std::string s = str;
     return trim(s, filter);
 }
 /// Print a two part "help" string
-inline std::ostream &format_help(std::ostream &out, std::string name, std::string description, size_t wid) {
+inline std::ostream &format_help(std::ostream &out, std::string name, std::string description, std::size_t wid) {
     name = "  " + name;
     out << std::setw(static_cast<int>(wid)) << std::left << name;
     if(!description.empty()) {
@@ -196,7 +212,7 @@ inline std::string remove_underscore(std::string str) {
 /// Find and replace a substring with another substring
 inline std::string find_and_replace(std::string str, std::string from, std::string to) {
 
-    size_t start_pos = 0;
+    std::size_t start_pos = 0;
 
     while((start_pos = str.find(from, start_pos)) != std::string::npos) {
         str.replace(start_pos, from.length(), to);
@@ -257,7 +273,7 @@ inline std::ptrdiff_t find_member(std::string name,
 /// Find a trigger string and call a modify callable function that takes the current string and starting position of the
 /// trigger and returns the position in the string to search for the next trigger string
 template <typename Callable> inline std::string find_and_modify(std::string str, std::string trigger, Callable modify) {
-    size_t start_pos = 0;
+    std::size_t start_pos = 0;
     while((start_pos = str.find(trigger, start_pos)) != std::string::npos) {
         start_pos = modify(str, start_pos);
     }
@@ -266,10 +282,12 @@ template <typename Callable> inline std::string find_and_modify(std::string str,
 
 /// Split a string '"one two" "three"' into 'one two', 'three'
 /// Quote characters can be ` ' or "
-inline std::vector<std::string> split_up(std::string str) {
+inline std::vector<std::string> split_up(std::string str, char delimiter = '\0') {
 
     const std::string delims("\'\"`");
-    auto find_ws = [](char ch) { return std::isspace<char>(ch, std::locale()); };
+    auto find_ws = [delimiter](char ch) {
+        return (delimiter == '\0') ? (std::isspace<char>(ch, std::locale()) != 0) : (ch == delimiter);
+    };
     trim(str);
 
     std::vector<std::string> output;
@@ -295,7 +313,7 @@ inline std::vector<std::string> split_up(std::string str) {
             if(it != std::end(str)) {
                 std::string value = std::string(str.begin(), it);
                 output.push_back(value);
-                str = std::string(it, str.end());
+                str = std::string(it + 1, str.end());
             } else {
                 output.push_back(str);
                 str = "";
@@ -315,7 +333,7 @@ inline std::vector<std::string> split_up(std::string str) {
 /// at the start of the first line). `"; "` would be for ini files
 ///
 /// Can't use Regex, or this would be a subs.
-inline std::string fix_newlines(std::string leader, std::string input) {
+inline std::string fix_newlines(const std::string &leader, std::string input) {
     std::string::size_type n = 0;
     while(n != std::string::npos && n < input.size()) {
         n = input.find('\n', n);
@@ -331,7 +349,7 @@ inline std::string fix_newlines(std::string leader, std::string input) {
 /// then modifies the string to replace the equality with a space.  This is needed
 /// to allow the split up function to work properly and is intended to be used with the find_and_modify function
 /// the return value is the offset+1 which is required by the find_and_modify function.
-inline size_t escape_detect(std::string &str, size_t offset) {
+inline std::size_t escape_detect(std::string &str, std::size_t offset) {
     auto next = str[offset + 1];
     if((next == '\"') || (next == '\'') || (next == '`')) {
         auto astart = str.find_last_of("-/ \"\'`", offset - 1);
diff --git a/packages/CLI11/include/CLI/Timer.hpp b/packages/CLI11/include/CLI/Timer.hpp
index 481546ab8c889c800ae88072bfa381926d47322d..1177bc13e0349baa359ea718b3823bc7c4561592 100644
--- a/packages/CLI11/include/CLI/Timer.hpp
+++ b/packages/CLI11/include/CLI/Timer.hpp
@@ -1,7 +1,10 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
 // On GCC < 4.8, the following define is often missing. Due to the
 // fact that this library only uses sleep_for, this should be safe
@@ -10,7 +13,7 @@
 #endif
 
 #include <array>
-#include <chrono>
+#include <chrono> // NOLINT(build/c++11)
 #include <functional>
 #include <iostream>
 #include <string>
@@ -40,7 +43,7 @@ class Timer {
     time_point start_;
 
     /// This is the number of times cycles (print divides by this number)
-    size_t cycles{1};
+    std::size_t cycles{1};
 
   public:
     /// Standard print function, this one is set by default
@@ -54,7 +57,7 @@ class Timer {
 
   public:
     /// Standard constructor, can set title and print function
-    Timer(std::string title = "Timer", time_print_t time_print = Simple)
+    explicit Timer(std::string title = "Timer", time_print_t time_print = Simple)
         : title_(std::move(title)), time_print_(std::move(time_print)), start_(clock::now()) {}
 
     /// Time a function by running it multiple times. Target time is the len to target.
@@ -63,7 +66,7 @@ class Timer {
         double total_time;
 
         start_ = clock::now();
-        size_t n = 0;
+        std::size_t n = 0;
         do {
             f();
             std::chrono::duration<double> elapsed = clock::now() - start_;
@@ -87,8 +90,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) {
-            std::array<char, 50> buffer;
-            std::snprintf(buffer.data(), 50, "%.5g", x);
+            const unsigned int buffer_length = 50;
+            std::array<char, buffer_length> buffer;
+            std::snprintf(buffer.data(), buffer_length, "%.5g", x);
             return buffer.data() + std::string(" ") + unit;
         };
 
@@ -101,13 +105,13 @@ class Timer {
         else
             return print_it(time, "s");
     }
-    // LCOV_EXCL_END
+    // LCOV_EXCL_STOP
 
     /// This is the main function, it creates a string
     std::string to_string() const { return time_print_(title_, make_time_str()); }
 
     /// Division sets the number of cycles to divide by (no graphical change)
-    Timer &operator/(size_t val) {
+    Timer &operator/(std::size_t val) {
         cycles = val;
         return *this;
     }
@@ -117,7 +121,7 @@ class Timer {
 class AutoTimer : public Timer {
   public:
     /// Reimplementing the constructor is required in GCC 4.7
-    AutoTimer(std::string title = "Timer", time_print_t time_print = Simple) : Timer(title, time_print) {}
+    explicit AutoTimer(std::string title = "Timer", time_print_t time_print = Simple) : Timer(title, time_print) {}
     // GCC 4.7 does not support using inheriting constructors.
 
     /// This destructor prints the string
diff --git a/packages/CLI11/include/CLI/TypeTools.hpp b/packages/CLI11/include/CLI/TypeTools.hpp
index 66a2909fefd6354c14e684f2af6f227baea8a0f6..115235bd18c1c3e26265e1071988f943e518c945 100644
--- a/packages/CLI11/include/CLI/TypeTools.hpp
+++ b/packages/CLI11/include/CLI/TypeTools.hpp
@@ -1,13 +1,18 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
 #include "StringTools.hpp"
+#include <cstdint>
 #include <exception>
 #include <memory>
 #include <string>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 namespace CLI {
@@ -46,6 +51,9 @@ template <typename T> struct is_vector : std::false_type {};
 /// Check to see if something is a vector (true if actually a vector)
 template <class T, class A> struct is_vector<std::vector<T, A>> : std::true_type {};
 
+/// Check to see if something is a vector (true if actually a const vector)
+template <class T, class A> struct is_vector<const std::vector<T, A>> : std::true_type {};
+
 /// Check to see if something is bool (fail check by default)
 template <typename T> struct is_bool : std::false_type {};
 
@@ -74,13 +82,16 @@ template <> struct IsMemberType<const char *> { using type = std::string; };
 
 namespace detail {
 
-// These are utilities for IsMember
+// These are utilities for IsMember and other transforming objects
 
 /// Handy helper to access the element_type generically. This is not part of is_copyable_ptr because it requires that
 /// pointer_traits<T> be valid.
-template <typename T> struct element_type {
-    using type =
-        typename std::conditional<is_copyable_ptr<T>::value, typename std::pointer_traits<T>::element_type, T>::type;
+
+/// not a pointer
+template <typename T, typename Enable = void> struct element_type { using type = T; };
+
+template <typename T> struct element_type<T, typename std::enable_if<is_copyable_ptr<T>::value>::type> {
+    using type = typename std::pointer_traits<T>::element_type;
 };
 
 /// Combination of the element type and value type - remove pointer (including smart pointers) and get the value_type of
@@ -137,7 +148,17 @@ struct pair_adaptor<
 // 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>());
+    static auto test(int, std::true_type) -> decltype(
+// NVCC warns about narrowing conversions here
+#ifdef __CUDACC__
+#pragma diag_suppress 2361
+#endif
+        TT { std::declval<CC>() }
+#ifdef __CUDACC__
+#pragma diag_default 2361
+#endif
+        ,
+        std::is_move_assignable<TT>());
 
     template <typename TT, typename CC> static auto test(int, std::false_type) -> std::false_type;
 
@@ -201,15 +222,24 @@ template <typename S> class is_tuple_like {
 };
 
 /// 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>
+template <typename T, enable_if_t<std::is_convertible<T, std::string>::value, detail::enabler> = detail::dummy>
 auto to_string(T &&value) -> decltype(std::forward<T>(value)) {
     return std::forward<T>(value);
 }
 
+/// Construct a string from the object
+template <typename T,
+          enable_if_t<std::is_constructible<std::string, T>::value && !std::is_convertible<T, std::string>::value,
+                      detail::enabler> = detail::dummy>
+std::string to_string(const T &value) {
+    return std::string(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>
+          enable_if_t<!std::is_convertible<std::string, T>::value && !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;
@@ -259,6 +289,22 @@ template <typename T1,
 std::string checked_to_string(T &&) {
     return std::string{};
 }
+/// get a string as a convertible value for arithmetic types
+template <typename T, enable_if_t<std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
+std::string value_string(const T &value) {
+    return std::to_string(value);
+}
+/// get a string as a convertible value for enumerations
+template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
+std::string value_string(const T &value) {
+    return std::to_string(static_cast<typename std::underlying_type<T>::type>(value));
+}
+/// for other types just use the regular to_string function
+template <typename T,
+          enable_if_t<!std::is_enum<T>::value && !std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
+auto value_string(const T &value) -> decltype(to_string(value)) {
+    return to_string(value);
+}
 
 /// This will only trigger for actual void type
 template <typename T, typename Enable = void> struct type_count { static const int value{0}; };
@@ -274,13 +320,28 @@ struct type_count<
     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};
+    static constexpr int value{is_vector<typename T::value_type>::value ? expected_max_vector_size
+                                                                        : type_count<typename T::value_type>::value};
+};
+
+/// This will only trigger for actual void type
+template <typename T, typename Enable = void> struct expected_count { static const int value{0}; };
+
+/// For most types the number of expected items is 1
+template <typename T>
+struct expected_count<T, typename std::enable_if<!is_vector<T>::value && !std::is_void<T>::value>::type> {
+    static constexpr int value{1};
+};
+/// number of expected items in a vector
+template <typename T> struct expected_count<T, typename std::enable_if<is_vector<T>::value>::type> {
+    static constexpr int value{expected_max_vector_size};
 };
 
 // Enumeration of the different supported categorizations of objects
-enum objCategory : int {
+enum class object_category : int {
     integral_value = 2,
     unsigned_integral = 4,
     enumeration = 6,
@@ -299,14 +360,16 @@ enum objCategory : int {
 };
 
 /// some type that is not otherwise recognized
-template <typename T, typename Enable = void> struct classify_object { static constexpr objCategory value{other}; };
+template <typename T, typename Enable = void> struct classify_object {
+    static constexpr object_category value{object_category::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};
+    static constexpr object_category value{object_category::integral_value};
 };
 
 /// Unsigned integers
@@ -314,17 +377,17 @@ 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};
+    static constexpr object_category value{object_category::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};
+    static constexpr object_category value{object_category::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};
+    static constexpr object_category value{object_category::floating_point};
 };
 
 /// String and similar direct assignment
@@ -333,7 +396,7 @@ 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};
+    static constexpr object_category value{object_category::string_assignable};
 };
 
 /// String and similar constructible and copy assignment
@@ -343,12 +406,12 @@ struct classify_object<
     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};
+    static constexpr object_category value{object_category::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};
+    static constexpr object_category value{object_category::enumeration};
 };
 
 /// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point,
@@ -369,7 +432,7 @@ 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};
+    static constexpr object_category value{object_category::number_constructible};
 };
 
 /// Assignable from int
@@ -378,7 +441,7 @@ 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};
+    static constexpr object_category value{object_category::integer_constructible};
 };
 
 /// Assignable from double
@@ -387,22 +450,22 @@ 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};
+    static constexpr object_category value{object_category::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};
+struct classify_object<T,
+                       typename std::enable_if<(type_count<T>::value >= 2 && !is_vector<T>::value) ||
+                                               (is_tuple_like<T>::value && uncommon_type<T>::value &&
+                                                !is_direct_constructible<T, double>::value &&
+                                                !is_direct_constructible<T, int>::value)>::type> {
+    static constexpr object_category value{object_category::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};
+    static constexpr object_category value{object_category::vector_value};
 };
 
 // Type name print
@@ -412,55 +475,54 @@ template <typename T> struct classify_object<T, typename std::enable_if<is_vecto
 /// But this is cleaner and works better in this case
 
 template <typename T,
-          enable_if_t<classify_object<T>::value == integral_value || classify_object<T>::value == integer_constructible,
+          enable_if_t<classify_object<T>::value == object_category::integral_value ||
+                          classify_object<T>::value == object_category::integer_constructible,
                       detail::enabler> = detail::dummy>
 constexpr const char *type_name() {
     return "INT";
 }
 
-template <typename T, enable_if_t<classify_object<T>::value == unsigned_integral, detail::enabler> = detail::dummy>
+template <typename T,
+          enable_if_t<classify_object<T>::value == object_category::unsigned_integral, detail::enabler> = detail::dummy>
 constexpr const char *type_name() {
     return "UINT";
 }
 
-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>
+template <typename T,
+          enable_if_t<classify_object<T>::value == object_category::floating_point ||
+                          classify_object<T>::value == object_category::number_constructible ||
+                          classify_object<T>::value == object_category::double_constructible,
+                      detail::enabler> = detail::dummy>
 constexpr const char *type_name() {
     return "FLOAT";
 }
 
 /// Print name for enumeration types
-template <typename T, enable_if_t<classify_object<T>::value == enumeration, detail::enabler> = detail::dummy>
+template <typename T,
+          enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
 constexpr const char *type_name() {
     return "ENUM";
 }
 
 /// Print name for enumeration types
-template <typename T, enable_if_t<classify_object<T>::value == boolean_value, detail::enabler> = detail::dummy>
+template <typename T,
+          enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
 constexpr const char *type_name() {
     return "BOOLEAN";
 }
 
 /// Print for all other types
-template <typename T, enable_if_t<classify_object<T>::value >= string_assignable, detail::enabler> = detail::dummy>
+template <typename T,
+          enable_if_t<classify_object<T>::value >= object_category::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() {
+template <typename T,
+          enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count<T>::value == 1,
+                      detail::enabler> = detail::dummy>
+inline std::string type_name() {
     return type_name<typename std::tuple_element<0, T>::type>();
 }
 
@@ -480,19 +542,26 @@ template <typename T, std::size_t I>
 }
 
 /// 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>
+template <typename T,
+          enable_if_t<classify_object<T>::value == object_category::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;
 }
 
+/// This one should not be used normally, since vector types print the internal type
+template <typename T,
+          enable_if_t<classify_object<T>::value == object_category::vector_value, detail::enabler> = detail::dummy>
+inline std::string type_name() {
+    return type_name<typename T::value_type>();
+}
+
 // Lexical cast
 
 /// Convert a flag into an integer value  typically binary flags
-inline int64_t to_flag_value(std::string val) {
+inline std::int64_t to_flag_value(std::string val) {
     static const std::string trueString("true");
     static const std::string falseString("false");
     if(val == trueString) {
@@ -502,10 +571,10 @@ inline int64_t to_flag_value(std::string val) {
         return -1;
     }
     val = detail::to_lower(val);
-    int64_t ret;
+    std::int64_t ret;
     if(val.size() == 1) {
         if(val[0] >= '1' && val[0] <= '9') {
-            return (static_cast<int64_t>(val[0]) - '0');
+            return (static_cast<std::int64_t>(val[0]) - '0');
         }
         switch(val[0]) {
         case '0':
@@ -535,13 +604,14 @@ inline int64_t to_flag_value(std::string val) {
 }
 
 /// Signed integers
-template <typename T, enable_if_t<classify_object<T>::value == integral_value, detail::enabler> = detail::dummy>
+template <typename T,
+          enable_if_t<classify_object<T>::value == object_category::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);
+        std::size_t n = 0;
+        std::int64_t output_ll = std::stoll(input, &n, 0);
         output = static_cast<T>(output_ll);
-        return n == input.size() && static_cast<long long>(output) == output_ll;
+        return n == input.size() && static_cast<std::int64_t>(output) == output_ll;
     } catch(const std::invalid_argument &) {
         return false;
     } catch(const std::out_of_range &) {
@@ -550,16 +620,17 @@ bool lexical_cast(const std::string &input, T &output) {
 }
 
 /// Unsigned integers
-template <typename T, enable_if_t<classify_object<T>::value == unsigned_integral, detail::enabler> = detail::dummy>
+template <typename T,
+          enable_if_t<classify_object<T>::value == object_category::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.
 
     try {
-        size_t n = 0;
-        unsigned long long output_ll = std::stoull(input, &n, 0);
+        std::size_t n = 0;
+        std::uint64_t output_ll = std::stoull(input, &n, 0);
         output = static_cast<T>(output_ll);
-        return n == input.size() && static_cast<unsigned long long>(output) == output_ll;
+        return n == input.size() && static_cast<std::uint64_t>(output) == output_ll;
     } catch(const std::invalid_argument &) {
         return false;
     } catch(const std::out_of_range &) {
@@ -568,7 +639,8 @@ bool lexical_cast(const std::string &input, T &output) {
 }
 
 /// Boolean values
-template <typename T, enable_if_t<classify_object<T>::value == boolean_value, detail::enabler> = detail::dummy>
+template <typename T,
+          enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
 bool lexical_cast(const std::string &input, T &output) {
     try {
         auto out = to_flag_value(input);
@@ -585,10 +657,11 @@ bool lexical_cast(const std::string &input, T &output) {
 }
 
 /// Floats
-template <typename T, enable_if_t<classify_object<T>::value == floating_point, detail::enabler> = detail::dummy>
+template <typename T,
+          enable_if_t<classify_object<T>::value == object_category::floating_point, detail::enabler> = detail::dummy>
 bool lexical_cast(const std::string &input, T &output) {
     try {
-        size_t n = 0;
+        std::size_t n = 0;
         output = static_cast<T>(std::stold(input, &n));
         return n == input.size();
     } catch(const std::invalid_argument &) {
@@ -599,21 +672,25 @@ bool lexical_cast(const 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>
+template <typename T,
+          enable_if_t<classify_object<T>::value == object_category::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>
+template <
+    typename T,
+    enable_if_t<classify_object<T>::value == object_category::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<classify_object<T>::value == enumeration, detail::enabler> = detail::dummy>
+template <typename T,
+          enable_if_t<classify_object<T>::value == object_category::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);
@@ -625,7 +702,9 @@ bool lexical_cast(const std::string &input, T &output) {
 }
 
 /// Assignable from double or int
-template <typename T, enable_if_t<classify_object<T>::value == number_constructible, detail::enabler> = detail::dummy>
+template <
+    typename T,
+    enable_if_t<classify_object<T>::value == object_category::number_constructible, detail::enabler> = detail::dummy>
 bool lexical_cast(const std::string &input, T &output) {
     int val;
     if(lexical_cast(input, val)) {
@@ -642,7 +721,9 @@ bool lexical_cast(const std::string &input, T &output) {
 }
 
 /// Assignable from int
-template <typename T, enable_if_t<classify_object<T>::value == integer_constructible, detail::enabler> = detail::dummy>
+template <
+    typename T,
+    enable_if_t<classify_object<T>::value == object_category::integer_constructible, detail::enabler> = detail::dummy>
 bool lexical_cast(const std::string &input, T &output) {
     int val;
     if(lexical_cast(input, val)) {
@@ -653,7 +734,9 @@ bool lexical_cast(const std::string &input, T &output) {
 }
 
 /// Assignable from double
-template <typename T, enable_if_t<classify_object<T>::value == double_constructible, detail::enabler> = detail::dummy>
+template <
+    typename T,
+    enable_if_t<classify_object<T>::value == object_category::double_constructible, detail::enabler> = detail::dummy>
 bool lexical_cast(const std::string &input, T &output) {
     double val;
     if(lexical_cast(input, val)) {
@@ -664,7 +747,7 @@ bool lexical_cast(const std::string &input, T &output) {
 }
 
 /// Non-string parsable by a stream
-template <typename T, enable_if_t<classify_object<T>::value == other, detail::enabler> = detail::dummy>
+template <typename T, enable_if_t<classify_object<T>::value == object_category::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 "
@@ -673,8 +756,27 @@ bool lexical_cast(const std::string &input, T &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>
+template <
+    typename T,
+    typename XC,
+    enable_if_t<std::is_same<T, XC>::value && (classify_object<T>::value == object_category::string_assignable ||
+                                               classify_object<T>::value == object_category::string_constructible),
+                detail::enabler> = detail::dummy>
+bool lexical_assign(const std::string &input, T &output) {
+    return lexical_cast(input, output);
+}
+
+/// Assign a value through lexical cast operations
+template <typename T,
+          typename XC,
+          enable_if_t<std::is_same<T, XC>::value && classify_object<T>::value != object_category::string_assignable &&
+                          classify_object<T>::value != object_category::string_constructible,
+                      detail::enabler> = detail::dummy>
 bool lexical_assign(const std::string &input, T &output) {
+    if(input.empty()) {
+        output = T{};
+        return true;
+    }
     return lexical_cast(input, output);
 }
 
@@ -684,8 +786,8 @@ template <
     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);
+    XC val{};
+    bool parse_result = (!input.empty()) ? lexical_cast<XC>(input, val) : true;
     if(parse_result) {
         output = val;
     }
@@ -699,18 +801,19 @@ template <typename T,
                           std::is_move_assignable<T>::value,
                       detail::enabler> = detail::dummy>
 bool lexical_assign(const std::string &input, T &output) {
-    XC val;
-    bool parse_result = lexical_cast<XC>(input, val);
+    XC val{};
+    bool parse_result = input.empty() ? true : 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>
+template <
+    typename T,
+    typename XC,
+    enable_if_t<!is_tuple_like<T>::value && !is_tuple_like<XC>::value && !is_vector<T>::value && !is_vector<XC>::value,
+                detail::enabler> = detail::dummy>
 bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
     return lexical_assign<T, XC>(strings[0], output);
 }
@@ -722,9 +825,9 @@ template <typename T,
 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);
+    bool retval = lexical_assign<decltype(v1), decltype(v1)>(strings[0], v1);
     if(strings.size() > 1) {
-        retval &= lexical_cast(strings[1], v2);
+        retval = retval && lexical_assign<decltype(v2), decltype(v2)>(strings[1], v2);
     }
     if(retval) {
         output = T{v1, v2};
@@ -735,23 +838,54 @@ bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
 /// 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>
+          enable_if_t<expected_count<T>::value == expected_max_vector_size &&
+                          expected_count<XC>::value == expected_max_vector_size && 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());
+        bool retval = lexical_assign<typename T::value_type, typename XC::value_type>(elem, output.back());
+        if(!retval) {
+            return false;
+        }
     }
-    return (!output.empty()) && retval;
+    return (!output.empty());
+}
+
+/// Lexical conversion of a vector types with type size of two
+template <class T,
+          class XC,
+          enable_if_t<expected_count<T>::value == expected_max_vector_size &&
+                          expected_count<XC>::value == expected_max_vector_size && type_count<XC>::value == 2,
+                      detail::enabler> = detail::dummy>
+bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
+    output.clear();
+    for(std::size_t ii = 0; ii < strings.size(); ii += 2) {
+
+        typename std::tuple_element<0, typename XC::value_type>::type v1;
+        typename std::tuple_element<1, typename XC::value_type>::type v2;
+        bool retval = lexical_assign<decltype(v1), decltype(v1)>(strings[ii], v1);
+        if(strings.size() > ii + 1) {
+            retval = retval && lexical_assign<decltype(v2), decltype(v2)>(strings[ii + 1], v2);
+        }
+        if(retval) {
+            output.emplace_back(v1, v2);
+        } else {
+            return false;
+        }
+    }
+    return (!output.empty());
 }
 
 /// 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>
+          enable_if_t<(expected_count<T>::value == expected_max_vector_size) && (expected_count<XC>::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();
@@ -759,10 +893,27 @@ bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
     for(const auto &elem : strings) {
 
         output.emplace_back();
-        retval &= lexical_assign<typename T::value_type, XC>(elem, output.back());
+        retval = retval && lexical_assign<typename T::value_type, XC>(elem, output.back());
     }
     return (!output.empty()) && retval;
 }
+// This one is last since it can call other lexical_conversion functions
+/// Lexical conversion if there is only one element but the conversion type is a vector
+template <typename T,
+          typename XC,
+          enable_if_t<!is_tuple_like<T>::value && !is_vector<T>::value && is_vector<XC>::value, detail::enabler> =
+              detail::dummy>
+bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
+
+    if(strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) {
+        XC val;
+        auto retval = lexical_conversion<XC, XC>(strings, val);
+        output = T{val};
+        return retval;
+    }
+    output = T{};
+    return true;
+}
 
 /// function template for converting tuples if the static Index is greater than the tuple size
 template <class T, class XC, std::size_t I>
@@ -776,12 +927,12 @@ template <class T, class XC, std::size_t I>
     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 = 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);
+    retval = retval && tuple_conversion<T, XC, I + 1>(strings, output);
     return retval;
 }
 
@@ -794,6 +945,38 @@ bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
     return tuple_conversion<T, XC, 0>(strings, output);
 }
 
+/// Lexical conversion of a vector types with type_size >2
+template <class T,
+          class XC,
+          enable_if_t<expected_count<T>::value == expected_max_vector_size &&
+                          expected_count<XC>::value == expected_max_vector_size && (type_count<XC>::value > 2),
+                      detail::enabler> = detail::dummy>
+bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
+    bool retval = true;
+    output.clear();
+    std::vector<std::string> temp;
+    std::size_t ii = 0;
+    std::size_t icount = 0;
+    std::size_t xcm = type_count<XC>::value;
+    while(ii < strings.size()) {
+        temp.push_back(strings[ii]);
+        ++ii;
+        ++icount;
+        if(icount == xcm || temp.back().empty()) {
+            if(static_cast<int>(xcm) == expected_max_vector_size) {
+                temp.pop_back();
+            }
+            output.emplace_back();
+            retval = retval && lexical_conversion<typename T::value_type, typename XC::value_type>(temp, output.back());
+            temp.clear();
+            if(!retval) {
+                return false;
+            }
+            icount = 0;
+        }
+    }
+    return retval;
+}
 /// 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
@@ -802,7 +985,7 @@ bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
 template <typename T,
           enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
 void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
-    int64_t count{0};
+    std::int64_t count{0};
     for(auto &flag : flags) {
         count += detail::to_flag_value(flag);
     }
@@ -817,7 +1000,7 @@ void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
 template <typename T,
           enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
 void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
-    int64_t count{0};
+    std::int64_t count{0};
     for(auto &flag : flags) {
         count += detail::to_flag_value(flag);
     }
diff --git a/packages/CLI11/include/CLI/Validators.hpp b/packages/CLI11/include/CLI/Validators.hpp
index 5f68423724c743346c8cf26e18b340a4a9201ae9..1159e53c2c2c7ab0b1c76e79e7c20d8f477a7abf 100644
--- a/packages/CLI11/include/CLI/Validators.hpp
+++ b/packages/CLI11/include/CLI/Validators.hpp
@@ -1,23 +1,54 @@
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
 #pragma once
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
 
+#include "CLI/Macros.hpp"
 #include "CLI/StringTools.hpp"
 #include "CLI/TypeTools.hpp"
 
 #include <cmath>
+#include <cstdint>
 #include <functional>
 #include <iostream>
 #include <limits>
 #include <map>
 #include <memory>
 #include <string>
+#include <utility>
+#include <vector>
+
+// [CLI11:verbatim]
 
 // C standard library
 // Only needed for existence checking
-// Could be swapped for filesystem in C++17
+#if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM
+#if __has_include(<filesystem>)
+// Filesystem cannot be used if targeting macOS < 10.15
+#if defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
+#define CLI11_HAS_FILESYSTEM 0
+#else
+#include <filesystem>
+#if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703
+#define CLI11_HAS_FILESYSTEM 1
+#else
+#define CLI11_HAS_FILESYSTEM 0
+#endif
+#endif
+#endif
+#endif
+
+#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
+#include <filesystem> // NOLINT(build/include)
+#else
 #include <sys/stat.h>
 #include <sys/types.h>
+#endif
+
+// [CLI11:verbatim]
 
 namespace CLI {
 
@@ -38,11 +69,11 @@ class Validator {
     /// This is the description function, if empty the description_ will be used
     std::function<std::string()> desc_function_{[]() { return std::string{}; }};
 
-    /// This it the base function that is to be called.
+    /// This is the base function that is to be called.
     /// Returns a string error message if validation fails.
     std::function<std::string(std::string &)> func_{[](std::string &) { return std::string{}; }};
     /// The name for search purposes of the Validator
-    std::string name_;
+    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
@@ -250,18 +281,61 @@ class CustomValidator : public Validator {
 // Therefore, this is in detail.
 namespace detail {
 
+/// CLI enumeration of different file types
+enum class path_type { nonexistant, file, directory };
+
+#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
+/// get the type of the path from a file name
+inline path_type check_path(const char *file) noexcept {
+    std::error_code ec;
+    auto stat = std::filesystem::status(file, ec);
+    if(ec) {
+        return path_type::nonexistant;
+    }
+    switch(stat.type()) {
+    case std::filesystem::file_type::none:
+    case std::filesystem::file_type::not_found:
+        return path_type::nonexistant;
+    case std::filesystem::file_type::directory:
+        return path_type::directory;
+    case std::filesystem::file_type::symlink:
+    case std::filesystem::file_type::block:
+    case std::filesystem::file_type::character:
+    case std::filesystem::file_type::fifo:
+    case std::filesystem::file_type::socket:
+    case std::filesystem::file_type::regular:
+    case std::filesystem::file_type::unknown:
+    default:
+        return path_type::file;
+    }
+}
+#else
+/// get the type of the path from a file name
+inline path_type check_path(const char *file) noexcept {
+#if defined(_MSC_VER)
+    struct __stat64 buffer;
+    if(_stat64(file, &buffer) == 0) {
+        return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
+    }
+#else
+    struct stat buffer;
+    if(stat(file, &buffer) == 0) {
+        return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
+    }
+#endif
+    return path_type::nonexistant;
+}
+#endif
 /// Check for an existing file (returns error message if check fails)
 class ExistingFileValidator : public Validator {
   public:
     ExistingFileValidator() : Validator("FILE") {
         func_ = [](std::string &filename) {
-            struct stat buffer;
-            bool exist = stat(filename.c_str(), &buffer) == 0;
-            bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
-            if(!exist) {
+            auto path_result = check_path(filename.c_str());
+            if(path_result == path_type::nonexistant) {
                 return "File does not exist: " + filename;
             }
-            if(is_dir) {
+            if(path_result == path_type::directory) {
                 return "File is actually a directory: " + filename;
             }
             return std::string();
@@ -274,13 +348,11 @@ class ExistingDirectoryValidator : public Validator {
   public:
     ExistingDirectoryValidator() : Validator("DIR") {
         func_ = [](std::string &filename) {
-            struct stat buffer;
-            bool exist = stat(filename.c_str(), &buffer) == 0;
-            bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
-            if(!exist) {
+            auto path_result = check_path(filename.c_str());
+            if(path_result == path_type::nonexistant) {
                 return "Directory does not exist: " + filename;
             }
-            if(!is_dir) {
+            if(path_result == path_type::file) {
                 return "Directory is actually a file: " + filename;
             }
             return std::string();
@@ -293,9 +365,8 @@ class ExistingPathValidator : public Validator {
   public:
     ExistingPathValidator() : Validator("PATH(existing)") {
         func_ = [](std::string &filename) {
-            struct stat buffer;
-            bool const exist = stat(filename.c_str(), &buffer) == 0;
-            if(!exist) {
+            auto path_result = check_path(filename.c_str());
+            if(path_result == path_type::nonexistant) {
                 return "Path does not exist: " + filename;
             }
             return std::string();
@@ -308,9 +379,8 @@ class NonexistentPathValidator : public Validator {
   public:
     NonexistentPathValidator() : Validator("PATH(non-existing)") {
         func_ = [](std::string &filename) {
-            struct stat buffer;
-            bool exist = stat(filename.c_str(), &buffer) == 0;
-            if(exist) {
+            auto path_result = check_path(filename.c_str());
+            if(path_result != path_type::nonexistant) {
                 return "Path already exists: " + filename;
             }
             return std::string();
@@ -325,17 +395,16 @@ class IPV4Validator : public Validator {
         func_ = [](std::string &ip_addr) {
             auto result = CLI::detail::split(ip_addr, '.');
             if(result.size() != 4) {
-                return "Invalid IPV4 address must have four parts " + ip_addr;
+                return std::string("Invalid IPV4 address must have four parts (") + ip_addr + ')';
             }
             int num;
-            bool retval = true;
             for(const auto &var : result) {
-                retval &= detail::lexical_cast(var, num);
+                bool retval = detail::lexical_cast(var, num);
                 if(!retval) {
-                    return "Failed parsing number " + var;
+                    return std::string("Failed parsing number (") + var + ')';
                 }
                 if(num < 0 || num > 255) {
-                    return "Each IP number must be between 0 and 255 " + var;
+                    return std::string("Each IP number must be between 0 and 255 ") + var;
                 }
             }
             return std::string();
@@ -343,31 +412,47 @@ class IPV4Validator : public Validator {
     }
 };
 
-/// Validate the argument is a number and greater than or equal to 0
+/// Validate the argument is a number and greater than 0
 class PositiveNumber : public Validator {
   public:
     PositiveNumber() : Validator("POSITIVE") {
         func_ = [](std::string &number_str) {
-            int number;
+            double number;
             if(!detail::lexical_cast(number_str, number)) {
-                return "Failed parsing number " + number_str;
+                return std::string("Failed parsing number: (") + number_str + ')';
+            }
+            if(number <= 0) {
+                return std::string("Number less or equal to 0: (") + number_str + ')';
+            }
+            return std::string();
+        };
+    }
+};
+/// Validate the argument is a number and greater than or equal to 0
+class NonNegativeNumber : public Validator {
+  public:
+    NonNegativeNumber() : Validator("NONNEGATIVE") {
+        func_ = [](std::string &number_str) {
+            double number;
+            if(!detail::lexical_cast(number_str, number)) {
+                return std::string("Failed parsing number: (") + number_str + ')';
             }
             if(number < 0) {
-                return "Number less then 0 " + number_str;
+                return std::string("Number less than 0: (") + number_str + ')';
             }
             return std::string();
         };
     }
 };
 
-/// Validate the argument is a number and greater than or equal to 0
+/// Validate the argument is a number
 class Number : public Validator {
   public:
     Number() : Validator("NUMBER") {
         func_ = [](std::string &number_str) {
             double number;
             if(!detail::lexical_cast(number_str, number)) {
-                return "Failed parsing as a number " + number_str;
+                return std::string("Failed parsing as a number (") + number_str + ')';
             }
             return std::string();
         };
@@ -396,6 +481,9 @@ const detail::IPV4Validator ValidIPV4;
 /// Check for a positive number
 const detail::PositiveNumber PositiveNumber;
 
+/// Check for a non-negative number
+const detail::NonNegativeNumber NonNegativeNumber;
+
 /// Check for a number
 const detail::Number Number;
 
@@ -415,7 +503,8 @@ class Range : public Validator {
             T val;
             bool converted = detail::lexical_cast(input, val);
             if((!converted) || (val < min || val > max))
-                return "Value " + input + " not in range " + std::to_string(min) + " to " + std::to_string(max);
+                return std::string("Value ") + input + " not in range " + std::to_string(min) + " to " +
+                       std::to_string(max);
 
             return std::string();
         };
@@ -441,7 +530,7 @@ class Bound : public Validator {
             T val;
             bool converted = detail::lexical_cast(input, val);
             if(!converted) {
-                return "Value " + input + " could not be converted";
+                return std::string("Value ") + input + " could not be converted";
             }
             if(val < min)
                 input = detail::to_string(min);
@@ -604,7 +693,7 @@ class IsMember : public Validator {
 
     /// This allows in-place construction using an initializer list
     template <typename T, typename... Args>
-    explicit IsMember(std::initializer_list<T> values, Args &&... args)
+    IsMember(std::initializer_list<T> values, Args &&... args)
         : IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}
 
     /// This checks to see if an item is in a set (empty function)
@@ -642,7 +731,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::to_string(detail::pair_adaptor<element_t>::first(*(res.second)));
+                    input = detail::value_string(detail::pair_adaptor<element_t>::first(*(res.second)));
                 }
 
                 // Return empty error string (success)
@@ -675,7 +764,7 @@ class Transformer : public Validator {
 
     /// This allows in-place construction
     template <typename... Args>
-    explicit Transformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&... args)
+    Transformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&... args)
         : Transformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
 
     /// direct map of std::string to std::string
@@ -711,7 +800,7 @@ class Transformer : public Validator {
             }
             auto res = detail::search(mapping, b, filter_fn);
             if(res.first) {
-                input = detail::to_string(detail::pair_adaptor<element_t>::second(*res.second));
+                input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
             }
             return std::string{};
         };
@@ -733,7 +822,7 @@ class CheckedTransformer : public Validator {
 
     /// This allows in-place construction
     template <typename... Args>
-    explicit CheckedTransformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&... args)
+    CheckedTransformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&... args)
         : CheckedTransformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
 
     /// direct map of std::string to std::string
@@ -779,12 +868,12 @@ class CheckedTransformer : public Validator {
                 }
                 auto res = detail::search(mapping, b, filter_fn);
                 if(res.first) {
-                    input = detail::to_string(detail::pair_adaptor<element_t>::second(*res.second));
+                    input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
                     return std::string{};
                 }
             }
             for(const auto &v : detail::smart_deref(mapping)) {
-                auto output_string = detail::to_string(detail::pair_adaptor<element_t>::second(v));
+                auto output_string = detail::value_string(detail::pair_adaptor<element_t>::second(v));
                 if(output_string == input) {
                     return std::string();
                 }
@@ -864,7 +953,7 @@ class AsNumberWithUnit : public Validator {
             }
 
             std::string unit{unit_begin, input.end()};
-            input.resize(static_cast<size_t>(std::distance(input.begin(), unit_begin)));
+            input.resize(static_cast<std::size_t>(std::distance(input.begin(), unit_begin)));
             detail::trim(input);
 
             if(opts & UNIT_REQUIRED && unit.empty()) {
@@ -876,7 +965,8 @@ class AsNumberWithUnit : public Validator {
 
             bool converted = detail::lexical_cast(input, num);
             if(!converted) {
-                throw ValidationError("Value " + input + " could not be converted to " + detail::type_name<Number>());
+                throw ValidationError(std::string("Value ") + input + " could not be converted to " +
+                                      detail::type_name<Number>());
             }
 
             if(unit.empty()) {
@@ -924,7 +1014,8 @@ class AsNumberWithUnit : public Validator {
             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);
+                    throw ValidationError(std::string("Several matching lowercase unit representations are found: ") +
+                                          s);
                 }
                 lower_mapping[detail::to_lower(kv.first)] = kv.second;
             }
@@ -958,7 +1049,7 @@ class AsNumberWithUnit : public Validator {
 ///   "2 EiB" => 2^61 // Units up to exibyte are supported
 class AsSizeValue : public AsNumberWithUnit {
   public:
-    using result_t = uint64_t;
+    using result_t = std::uint64_t;
 
     /// If kb_is_1000 is true,
     /// interpret 'kb', 'k' as 1000 and 'kib', 'ki' as 1024
@@ -1017,7 +1108,7 @@ inline std::pair<std::string, std::string> split_program_name(std::string comman
     std::pair<std::string, std::string> vals;
     trim(commandline);
     auto esp = commandline.find_first_of(' ', 1);
-    while(!ExistingFile(commandline.substr(0, esp)).empty()) {
+    while(detail::check_path(commandline.substr(0, esp).c_str()) != path_type::file) {
         esp = commandline.find_first_of(' ', esp + 1);
         if(esp == std::string::npos) {
             // if we have reached the end and haven't found a valid file just assume the first argument is the
diff --git a/packages/CLI11/include/CLI/Version.hpp b/packages/CLI11/include/CLI/Version.hpp
index e9261b8951fde2a1db1ed6069de359a4e13cc2c4..3bfd8899df64165a9b358d9f76979257906533ad 100644
--- a/packages/CLI11/include/CLI/Version.hpp
+++ b/packages/CLI11/include/CLI/Version.hpp
@@ -1,13 +1,16 @@
-#pragma once
+// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+#pragma once
 
 // [CLI11:verbatim]
 
 #define CLI11_VERSION_MAJOR 1
-#define CLI11_VERSION_MINOR 8
+#define CLI11_VERSION_MINOR 9
 #define CLI11_VERSION_PATCH 0
-#define CLI11_VERSION "1.8.0"
+#define CLI11_VERSION "1.9.0"
 
 // [CLI11:verbatim]
diff --git a/packages/CLI11/tests/AppTest.cpp b/packages/CLI11/tests/AppTest.cpp
index 63a2f67b4aa55b49f4a1dbd981c6d9b63da19df9..633072ee836734196bb2a8249b41bbb405de6207 100644
--- a/packages/CLI11/tests/AppTest.cpp
+++ b/packages/CLI11/tests/AppTest.cpp
@@ -1,5 +1,6 @@
 #include "app_helper.hpp"
 #include <complex>
+#include <cstdint>
 #include <cstdlib>
 
 #include "gmock/gmock.h"
@@ -40,12 +41,14 @@ TEST_F(TApp, OneFlagShortValuesAs) {
     flg->take_last();
     EXPECT_EQ(opt->as<int>(), 2);
     flg->multi_option_policy(CLI::MultiOptionPolicy::Throw);
-    EXPECT_THROW(opt->as<int>(), CLI::ConversionError);
-
+    EXPECT_THROW(opt->as<int>(), CLI::ArgumentMismatch);
+    flg->multi_option_policy(CLI::MultiOptionPolicy::TakeAll);
     auto vec = opt->as<std::vector<int>>();
     EXPECT_EQ(vec[0], 1);
     EXPECT_EQ(vec[1], 2);
     flg->multi_option_policy(CLI::MultiOptionPolicy::Join);
+    EXPECT_EQ(opt->as<std::string>(), "1\n2");
+    flg->delimiter(',');
     EXPECT_EQ(opt->as<std::string>(), "1,2");
 }
 
@@ -138,7 +141,7 @@ TEST_F(TApp, RequireOptionsError) {
 }
 
 TEST_F(TApp, BoolFlagOverride) {
-    bool val;
+    bool val{false};
     auto flg = app.add_flag("--this,--that", val);
 
     app.parse("--this");
@@ -158,7 +161,7 @@ TEST_F(TApp, BoolFlagOverride) {
 }
 
 TEST_F(TApp, OneFlagRef) {
-    int ref;
+    int ref{0};
     app.add_flag("-c,--count", ref);
     args = {"--count"};
     run();
@@ -168,7 +171,7 @@ TEST_F(TApp, OneFlagRef) {
 }
 
 TEST_F(TApp, OneFlagRefValue) {
-    int ref;
+    int ref{0};
     app.add_flag("-c,--count", ref);
     args = {"--count=7"};
     run();
@@ -178,7 +181,7 @@ TEST_F(TApp, OneFlagRefValue) {
 }
 
 TEST_F(TApp, OneFlagRefValueFalse) {
-    int ref;
+    int ref{0};
     auto flg = app.add_flag("-c,--count", ref);
     args = {"--count=false"};
     run();
@@ -198,7 +201,7 @@ TEST_F(TApp, OneFlagRefValueFalse) {
 }
 
 TEST_F(TApp, FlagNegation) {
-    int ref;
+    int ref{0};
     auto flg = app.add_flag("-c,--count,--ncount{false}", ref);
     args = {"--count", "-c", "--ncount"};
     EXPECT_FALSE(flg->check_fname("count"));
@@ -211,7 +214,7 @@ TEST_F(TApp, FlagNegation) {
 }
 
 TEST_F(TApp, FlagNegationShortcutNotation) {
-    int ref;
+    int ref{0};
     app.add_flag("-c,--count{true},!--ncount", ref);
     args = {"--count=TRUE", "-c", "--ncount"};
     run();
@@ -222,7 +225,7 @@ TEST_F(TApp, FlagNegationShortcutNotation) {
 }
 
 TEST_F(TApp, FlagNegationShortcutNotationInvalid) {
-    int ref;
+    int ref{0};
     app.add_flag("-c,--count,!--ncount", ref);
     args = {"--ncount=happy"};
     EXPECT_THROW(run(), CLI::ConversionError);
@@ -292,7 +295,7 @@ TEST_F(TApp, OneStringEqualVersionSingleString) {
 TEST_F(TApp, OneStringEqualVersionSingleStringQuoted) {
     std::string str;
     app.add_option("-s,--string", str);
-    app.parse("--string=\"this is my quoted string\"");
+    app.parse(R"raw(--string="this is my quoted string")raw");
     EXPECT_EQ(1u, app.count("-s"));
     EXPECT_EQ(1u, app.count("--string"));
     EXPECT_EQ(str, "this is my quoted string");
@@ -303,7 +306,7 @@ TEST_F(TApp, OneStringEqualVersionSingleStringQuotedMultiple) {
     app.add_option("-s,--string", str);
     app.add_option("-t,--tstr", str2);
     app.add_option("-m,--mstr", str3);
-    app.parse("--string=\"this is my quoted string\" -t 'qstring 2' -m=`\"quoted string\"`");
+    app.parse(R"raw(--string="this is my quoted string" -t 'qstring 2' -m=`"quoted string"`)raw");
     EXPECT_EQ(str, "this is my quoted string");
     EXPECT_EQ(str2, "qstring 2");
     EXPECT_EQ(str3, "\"quoted string\"");
@@ -314,12 +317,12 @@ TEST_F(TApp, OneStringEqualVersionSingleStringEmbeddedEqual) {
     app.add_option("-s,--string", str);
     app.add_option("-t,--tstr", str2);
     app.add_option("-m,--mstr", str3);
-    app.parse("--string=\"app=\\\"test1 b\\\" test2=\\\"frogs\\\"\" -t 'qstring 2' -m=`\"quoted string\"`");
+    app.parse(R"raw(--string="app=\"test1 b\" test2=\"frogs\"" -t 'qstring 2' -m=`"quoted string"`)raw");
     EXPECT_EQ(str, "app=\"test1 b\" test2=\"frogs\"");
     EXPECT_EQ(str2, "qstring 2");
     EXPECT_EQ(str3, "\"quoted string\"");
 
-    app.parse("--string=\"app='test1 b' test2='frogs'\" -t 'qstring 2' -m=`\"quoted string\"`");
+    app.parse(R"raw(--string="app='test1 b' test2='frogs'" -t 'qstring 2' -m=`"quoted string"`)raw");
     EXPECT_EQ(str, "app='test1 b' test2='frogs'");
     EXPECT_EQ(str2, "qstring 2");
     EXPECT_EQ(str3, "\"quoted string\"");
@@ -331,12 +334,12 @@ TEST_F(TApp, OneStringEqualVersionSingleStringEmbeddedEqualWindowsStyle) {
     app.add_option("-t,--tstr", str2);
     app.add_option("--mstr", str3);
     app.allow_windows_style_options();
-    app.parse("/string:\"app:\\\"test1 b\\\" test2:\\\"frogs\\\"\" /t 'qstring 2' /mstr:`\"quoted string\"`");
+    app.parse(R"raw(/string:"app:\"test1 b\" test2:\"frogs\"" /t 'qstring 2' /mstr:`"quoted string"`)raw");
     EXPECT_EQ(str, "app:\"test1 b\" test2:\"frogs\"");
     EXPECT_EQ(str2, "qstring 2");
     EXPECT_EQ(str3, "\"quoted string\"");
 
-    app.parse("/string:\"app:'test1 b' test2:'frogs'\" /t 'qstring 2' /mstr:`\"quoted string\"`");
+    app.parse(R"raw(/string:"app:'test1 b' test2:'frogs'" /t 'qstring 2' /mstr:`"quoted string"`)raw");
     EXPECT_EQ(str, "app:'test1 b' test2:'frogs'");
     EXPECT_EQ(str2, "qstring 2");
     EXPECT_EQ(str3, "\"quoted string\"");
@@ -348,7 +351,7 @@ TEST_F(TApp, OneStringEqualVersionSingleStringQuotedMultipleMixedStyle) {
     app.add_option("-t,--tstr", str2);
     app.add_option("-m,--mstr", str3);
     app.allow_windows_style_options();
-    app.parse("/string:\"this is my quoted string\" /t 'qstring 2' -m=`\"quoted string\"`");
+    app.parse(R"raw(/string:"this is my quoted string" /t 'qstring 2' -m=`"quoted string"`)raw");
     EXPECT_EQ(str, "this is my quoted string");
     EXPECT_EQ(str2, "qstring 2");
     EXPECT_EQ(str3, "\"quoted string\"");
@@ -359,7 +362,7 @@ TEST_F(TApp, OneStringEqualVersionSingleStringQuotedMultipleInMiddle) {
     app.add_option("-s,--string", str);
     app.add_option("-t,--tstr", str2);
     app.add_option("-m,--mstr", str3);
-    app.parse(R"raw(--string="this is my quoted string" -t "qst\"ring 2" -m=`"quoted string"`")raw");
+    app.parse(R"raw(--string="this is my quoted string" -t "qst\"ring 2" -m=`"quoted string"`)raw");
     EXPECT_EQ(str, "this is my quoted string");
     EXPECT_EQ(str2, "qst\"ring 2");
     EXPECT_EQ(str3, "\"quoted string\"");
@@ -382,7 +385,7 @@ TEST_F(TApp, OneStringEqualVersionSingleStringQuotedMultipleWithEqual) {
     app.add_option("-t,--tstr", str2);
     app.add_option("-m,--mstr", str3);
     app.add_option("-j,--jstr", str4);
-    app.parse("--string=\"this is my quoted string\" -t 'qstring 2' -m=`\"quoted string\"` --jstr=Unquoted");
+    app.parse(R"raw(--string="this is my quoted string" -t 'qstring 2' -m=`"quoted string"` --jstr=Unquoted)raw");
     EXPECT_EQ(str, "this is my quoted string");
     EXPECT_EQ(str2, "qstring 2");
     EXPECT_EQ(str3, "\"quoted string\"");
@@ -395,16 +398,42 @@ TEST_F(TApp, OneStringEqualVersionSingleStringQuotedMultipleWithEqualAndProgram)
     app.add_option("-t,--tstr", str2);
     app.add_option("-m,--mstr", str3);
     app.add_option("-j,--jstr", str4);
-    app.parse("program --string=\"this is my quoted string\" -t 'qstring 2' -m=`\"quoted string\"` --jstr=Unquoted",
-              true);
+    app.parse(
+        R"raw(program --string="this is my quoted string" -t 'qstring 2' -m=`"quoted string"` --jstr=Unquoted)raw",
+        true);
     EXPECT_EQ(str, "this is my quoted string");
     EXPECT_EQ(str2, "qstring 2");
     EXPECT_EQ(str3, "\"quoted string\"");
     EXPECT_EQ(str4, "Unquoted");
 }
 
+TEST_F(TApp, OneStringFlagLike) {
+    std::string str{"something"};
+    app.add_option("-s,--string", str)->expected(0, 1);
+    args = {"--string"};
+    run();
+    EXPECT_EQ(1u, app.count("-s"));
+    EXPECT_EQ(1u, app.count("--string"));
+    EXPECT_TRUE(str.empty());
+}
+
+TEST_F(TApp, OneIntFlagLike) {
+    int val{0};
+    auto opt = app.add_option("-i", val)->expected(0, 1);
+    args = {"-i"};
+    run();
+    EXPECT_EQ(1u, app.count("-i"));
+    opt->default_str("7");
+    run();
+    EXPECT_EQ(val, 7);
+
+    opt->default_val(9);
+    run();
+    EXPECT_EQ(val, 9);
+}
+
 TEST_F(TApp, TogetherInt) {
-    int i;
+    int i{0};
     app.add_option("-i,--int", i);
     args = {"-i4"};
     run();
@@ -416,7 +445,7 @@ TEST_F(TApp, TogetherInt) {
 }
 
 TEST_F(TApp, SepInt) {
-    int i;
+    int i{0};
     app.add_option("-i,--int", i);
     args = {"-i", "4"};
     run();
@@ -446,7 +475,7 @@ TEST_F(TApp, OneStringFunction) {
 }
 
 TEST_F(TApp, doubleFunction) {
-    double res;
+    double res{0.0};
     app.add_option_function<double>("--val", [&res](double val) { res = std::abs(val + 54); });
     args = {"--val", "-354.356"};
     run();
@@ -490,6 +519,33 @@ TEST_F(TApp, doubleVectorFunctionFail) {
     EXPECT_EQ(strvec.size(), 3u);
 }
 
+TEST_F(TApp, doubleVectorFunctionRunCallbackOnDefault) {
+    std::vector<double> res;
+    auto opt = app.add_option_function<std::vector<double>>("--val", [&res](const std::vector<double> &val) {
+        res = val;
+        std::transform(res.begin(), res.end(), res.begin(), [](double v) { return v + 5.0; });
+    });
+    args = {"--val", "5", "--val", "6", "--val", "7"};
+    run();
+    EXPECT_EQ(res.size(), 3u);
+    EXPECT_EQ(res[0], 10.0);
+    EXPECT_EQ(res[2], 12.0);
+    EXPECT_FALSE(opt->get_run_callback_for_default());
+    opt->run_callback_for_default();
+    opt->default_val(std::vector<int>{2, 1, -2});
+    EXPECT_EQ(res[0], 7.0);
+    EXPECT_EQ(res[2], 3.0);
+
+    EXPECT_THROW(opt->default_val("this is a string"), CLI::ConversionError);
+    auto vec = opt->as<std::vector<double>>();
+    ASSERT_EQ(vec.size(), 3U);
+    EXPECT_EQ(vec[0], 5.0);
+    EXPECT_EQ(vec[2], 7.0);
+    opt->check(CLI::Number);
+    opt->run_callback_for_default(false);
+    EXPECT_THROW(opt->default_val("this is a string"), CLI::ValidationError);
+}
+
 TEST_F(TApp, DefaultStringAgain) {
     std::string str = "previous";
     app.add_option("-s,--string", str);
@@ -540,7 +596,7 @@ TEST_F(TApp, LotsOfFlags) {
 
 TEST_F(TApp, NumberFlags) {
 
-    int val;
+    int val{0};
     app.add_flag("-1{1},-2{2},-3{3},-4{4},-5{5},-6{6}, -7{7}, -8{8}, -9{9}", val);
 
     args = {"-7"};
@@ -551,7 +607,7 @@ TEST_F(TApp, NumberFlags) {
 
 TEST_F(TApp, DisableFlagOverrideTest) {
 
-    int val;
+    int val{0};
     auto opt = app.add_flag("--1{1},--2{2},--3{3},--4{4},--5{5},--6{6}, --7{7}, --8{8}, --9{9}", val);
     EXPECT_FALSE(opt->get_disable_flag_override());
     opt->disable_flag_override();
@@ -593,9 +649,9 @@ TEST_F(TApp, LotsOfFlagsSingleStringExtraSpace) {
 
 TEST_F(TApp, BoolAndIntFlags) {
 
-    bool bflag;
-    int iflag;
-    unsigned int uflag;
+    bool bflag{false};
+    int iflag{0};
+    unsigned int uflag{0};
 
     app.add_flag("-b", bflag);
     app.add_flag("-i", iflag);
@@ -620,8 +676,44 @@ TEST_F(TApp, BoolAndIntFlags) {
     EXPECT_EQ((unsigned int)2, uflag);
 }
 
+TEST_F(TApp, FlagLikeOption) {
+    bool val{false};
+    auto opt = app.add_option("--flag", val)->type_size(0)->default_str("true");
+    args = {"--flag"};
+    run();
+    EXPECT_EQ(1u, app.count("--flag"));
+    EXPECT_TRUE(val);
+    val = false;
+    opt->type_size(0, 0); // should be the same as above
+    EXPECT_EQ(opt->get_type_size_min(), 0);
+    EXPECT_EQ(opt->get_type_size_max(), 0);
+    run();
+    EXPECT_EQ(1u, app.count("--flag"));
+    EXPECT_TRUE(val);
+}
+
+TEST_F(TApp, FlagLikeIntOption) {
+    int val{-47};
+    auto opt = app.add_option("--flag", val)->expected(0, 1);
+    // normally some default value should be set, but this test is for some paths in the validators checks to skip
+    // validation on empty string if nothing is expected
+    opt->check(CLI::PositiveNumber);
+    args = {"--flag"};
+    EXPECT_TRUE(opt->as<std::string>().empty());
+    run();
+    EXPECT_EQ(1u, app.count("--flag"));
+    EXPECT_NE(val, -47);
+    args = {"--flag", "12"};
+    run();
+
+    EXPECT_EQ(val, 12);
+    args.clear();
+    run();
+    EXPECT_TRUE(opt->as<std::string>().empty());
+}
+
 TEST_F(TApp, BoolOnlyFlag) {
-    bool bflag;
+    bool bflag{false};
     app.add_flag("-b", bflag)->multi_option_policy(CLI::MultiOptionPolicy::Throw);
 
     args = {"-b"};
@@ -629,11 +721,11 @@ TEST_F(TApp, BoolOnlyFlag) {
     EXPECT_TRUE(bflag);
 
     args = {"-b", "-b"};
-    EXPECT_THROW(run(), CLI::ConversionError);
+    EXPECT_THROW(run(), CLI::ArgumentMismatch);
 }
 
 TEST_F(TApp, BoolOption) {
-    bool bflag;
+    bool bflag{false};
     app.add_option("-b", bflag);
 
     args = {"-b", "false"};
@@ -660,7 +752,7 @@ TEST_F(TApp, BoolOption) {
 
 TEST_F(TApp, ShortOpts) {
 
-    unsigned long long funnyint;
+    unsigned long long funnyint{0};
     std::string someopt;
     app.add_flag("-z", funnyint);
     app.add_option("-y", someopt);
@@ -680,7 +772,7 @@ TEST_F(TApp, ShortOpts) {
 
 TEST_F(TApp, TwoParamTemplateOpts) {
 
-    double funnyint;
+    double funnyint{0.0};
     auto opt = app.add_option<double, unsigned int>("-y", funnyint);
 
     args = {"-y", "32"};
@@ -701,7 +793,7 @@ TEST_F(TApp, TwoParamTemplateOpts) {
 
 TEST_F(TApp, DefaultOpts) {
 
-    int i = 3;
+    int i{3};
     std::string s = "HI";
 
     app.add_option("-i,i", i);
@@ -800,9 +892,51 @@ TEST_F(TApp, TakeLastOptMulti) {
     EXPECT_EQ(vals, std::vector<int>({2, 3}));
 }
 
+TEST_F(TApp, vectorDefaults) {
+    std::vector<int> vals{4, 5};
+    auto opt = app.add_option("--long", vals, "", true);
+
+    args = {"--long", "[1,2,3]"};
+
+    run();
+
+    EXPECT_EQ(vals, std::vector<int>({1, 2, 3}));
+
+    args.clear();
+    run();
+    auto res = app["--long"]->as<std::vector<int>>();
+    EXPECT_EQ(res, std::vector<int>({4, 5}));
+
+    app.clear();
+    opt->expected(1)->take_last();
+    res = app["--long"]->as<std::vector<int>>();
+    EXPECT_EQ(res, std::vector<int>({5}));
+    opt->take_first();
+    res = app["--long"]->as<std::vector<int>>();
+    EXPECT_EQ(res, std::vector<int>({4}));
+
+    opt->expected(0, 1)->take_last();
+    run();
+
+    EXPECT_EQ(res, std::vector<int>({4}));
+    res = app["--long"]->as<std::vector<int>>();
+    EXPECT_EQ(res, std::vector<int>({5}));
+}
+
+TEST_F(TApp, TakeLastOptMulti_alternative_path) {
+    std::vector<int> vals;
+    app.add_option("--long", vals)->expected(2, -1)->take_last();
+
+    args = {"--long", "1", "2", "3"};
+
+    run();
+
+    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();
+    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));
@@ -826,7 +960,7 @@ TEST_F(TApp, TakeFirstOptMulti) {
 
 TEST_F(TApp, ComplexOptMulti) {
     std::complex<double> val;
-    app.add_complex("--long", val)->take_first();
+    app.add_complex("--long", val)->take_first()->allow_extra_args();
 
     args = {"--long", "1", "2", "3", "4"};
 
@@ -837,7 +971,7 @@ TEST_F(TApp, ComplexOptMulti) {
 }
 
 TEST_F(TApp, MissingValueNonRequiredOpt) {
-    int count;
+    int count{0};
     app.add_option("-c,--count", count);
 
     args = {"-c"};
@@ -1057,7 +1191,7 @@ TEST_F(TApp, RequiredPositionalVector) {
 // Tests positionals at end
 TEST_F(TApp, RequiredPositionalValidation) {
     std::vector<std::string> sources;
-    int dest;
+    int dest; // required
     std::string d2;
     app.add_option("src", sources);
     app.add_option("dest", dest)->required()->check(CLI::PositiveNumber);
@@ -1141,7 +1275,7 @@ TEST_F(TApp, RequiredOptsUnlimited) {
 
     app.allow_extras(false);
     std::vector<std::string> remain;
-    app.add_option("positional", remain);
+    auto popt = app.add_option("positional", remain);
     run();
     EXPECT_EQ(strs, std::vector<std::string>({"one", "two"}));
     EXPECT_EQ(remain, std::vector<std::string>());
@@ -1157,6 +1291,12 @@ TEST_F(TApp, RequiredOptsUnlimited) {
     run();
     EXPECT_EQ(strs, std::vector<std::string>({"two"}));
     EXPECT_EQ(remain, std::vector<std::string>({"one"}));
+
+    args = {"--str", "one", "two"};
+    popt->required();
+    run();
+    EXPECT_EQ(strs, std::vector<std::string>({"one"}));
+    EXPECT_EQ(remain, std::vector<std::string>({"two"}));
 }
 
 TEST_F(TApp, RequiredOptsUnlimitedShort) {
@@ -1217,9 +1357,9 @@ TEST_F(TApp, OptsUnlimitedEnd) {
 TEST_F(TApp, RequireOptPriority) {
 
     std::vector<std::string> strs;
-    app.add_option("--str", strs)->required();
+    app.add_option("--str", strs);
     std::vector<std::string> remain;
-    app.add_option("positional", remain)->expected(2);
+    app.add_option("positional", remain)->expected(2)->required();
 
     args = {"--str", "one", "two", "three"};
     run();
@@ -1239,7 +1379,7 @@ TEST_F(TApp, RequireOptPriorityShort) {
     std::vector<std::string> strs;
     app.add_option("-s", strs)->required();
     std::vector<std::string> remain;
-    app.add_option("positional", remain)->expected(2);
+    app.add_option("positional", remain)->expected(2)->required();
 
     args = {"-s", "one", "two", "three"};
     run();
@@ -1292,9 +1432,9 @@ TEST_F(TApp, RequiredFlags) {
 
 TEST_F(TApp, CallbackFlags) {
 
-    int64_t value = 0;
+    std::int64_t value{0};
 
-    auto func = [&value](int64_t x) { value = x; };
+    auto func = [&value](std::int64_t x) { value = x; };
 
     app.add_flag_function("-v", func);
 
@@ -1314,7 +1454,7 @@ TEST_F(TApp, CallbackFlags) {
 
 TEST_F(TApp, CallbackBoolFlags) {
 
-    bool value = false;
+    bool value{false};
 
     auto func = [&value]() { value = true; };
 
@@ -1330,13 +1470,13 @@ TEST_F(TApp, CallbackBoolFlags) {
     EXPECT_THROW(app.add_flag_callback("hi", func), CLI::IncorrectConstruction);
     cback->multi_option_policy(CLI::MultiOptionPolicy::Throw);
     args = {"--val", "--val=false"};
-    EXPECT_THROW(run(), CLI::ConversionError);
+    EXPECT_THROW(run(), CLI::ArgumentMismatch);
 }
 
 TEST_F(TApp, CallbackFlagsFalse) {
-    int64_t value = 0;
+    std::int64_t value = 0;
 
-    auto func = [&value](int64_t x) { value = x; };
+    auto func = [&value](std::int64_t x) { value = x; };
 
     app.add_flag_function("-v,-f{false},--val,--fval{false}", func);
 
@@ -1363,9 +1503,9 @@ TEST_F(TApp, CallbackFlagsFalse) {
 }
 
 TEST_F(TApp, CallbackFlagsFalseShortcut) {
-    int64_t value = 0;
+    std::int64_t value = 0;
 
-    auto func = [&value](int64_t x) { value = x; };
+    auto func = [&value](std::int64_t x) { value = x; };
 
     app.add_flag_function("-v,!-f,--val,!--fval", func);
 
@@ -1394,9 +1534,9 @@ TEST_F(TApp, CallbackFlagsFalseShortcut) {
 #if __cplusplus >= 201402L || _MSC_VER >= 1900
 TEST_F(TApp, CallbackFlagsAuto) {
 
-    int64_t value = 0;
+    std::int64_t value{0};
 
-    auto func = [&value](int64_t x) { value = x; };
+    auto func = [&value](std::int64_t x) { value = x; };
 
     app.add_flag("-v", func);
 
@@ -1453,7 +1593,7 @@ TEST_F(TApp, ForcedPositional) {
 
 TEST_F(TApp, MixedPositionals) {
 
-    int positional_int;
+    int positional_int{0};
     std::string positional_string;
     app.add_option("posit1,--posit1", positional_int, "");
     app.add_option("posit2,--posit2", positional_string, "");
@@ -1486,7 +1626,7 @@ TEST_F(TApp, BigPositional) {
 TEST_F(TApp, Reset) {
 
     app.add_flag("--simple");
-    double doub;
+    double doub{0.0};
     app.add_option("-d,--double", doub);
 
     args = {"--simple", "--double", "1.2"};
@@ -1638,7 +1778,7 @@ TEST_F(TApp, pair_check) {
 }
 
 // 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
@@ -1663,7 +1803,7 @@ TEST_F(TApp, pair_check_take_first) {
 
     EXPECT_THROW(run(), CLI::ValidationError);
 }
-*/
+
 TEST_F(TApp, VectorFixedString) {
     std::vector<std::string> strvec;
     std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
@@ -1710,7 +1850,7 @@ TEST_F(TApp, VectorIndexedValidator) {
 
 TEST_F(TApp, DefaultedResult) {
     std::string sval = "NA";
-    int ival;
+    int ival{0};
     auto opts = app.add_option("--string", sval)->capture_default_str();
     auto optv = app.add_option("--val", ival);
     args = {};
@@ -1720,7 +1860,7 @@ TEST_F(TApp, DefaultedResult) {
     opts->results(nString);
     EXPECT_EQ(nString, "NA");
     int newIval;
-    EXPECT_THROW(optv->results(newIval), CLI::ConversionError);
+    // EXPECT_THROW(optv->results(newIval), CLI::ConversionError);
     optv->default_str("442");
     optv->results(newIval);
     EXPECT_EQ(newIval, 442);
@@ -1731,7 +1871,8 @@ TEST_F(TApp, VectorUnlimString) {
     std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
 
     CLI::Option *opt = app.add_option("-s,--string", strvec);
-    EXPECT_EQ(-1, opt->get_expected());
+    EXPECT_EQ(1, opt->get_expected());
+    EXPECT_EQ(CLI::detail::expected_max_vector_size, opt->get_expected_max());
 
     args = {"--string", "mystring", "mystring2", "mystring3"};
     run();
@@ -1744,6 +1885,56 @@ TEST_F(TApp, VectorUnlimString) {
     EXPECT_EQ(answer, strvec);
 }
 
+// From https://github.com/CLIUtils/CLI11/issues/420
+TEST_F(TApp, stringLikeTests) {
+    struct nType {
+        explicit nType(const std::string &a_value) : m_value{a_value} {}
+
+        explicit operator std::string() const { return std::string{"op str"}; }
+
+        std::string m_value;
+    };
+
+    nType m_type{"abc"};
+    app.add_option("--type", m_type, "type")->capture_default_str();
+    run();
+
+    EXPECT_EQ(app["--type"]->as<std::string>(), "op str");
+    args = {"--type", "bca"};
+    run();
+    EXPECT_EQ(std::string(m_type), "op str");
+    EXPECT_EQ(m_type.m_value, "bca");
+}
+
+TEST_F(TApp, VectorExpectedRange) {
+    std::vector<std::string> strvec;
+
+    CLI::Option *opt = app.add_option("--string", strvec);
+    opt->expected(2, 4)->multi_option_policy(CLI::MultiOptionPolicy::Throw);
+
+    args = {"--string", "mystring", "mystring2", "mystring3"};
+    run();
+    EXPECT_EQ(3u, app.count("--string"));
+
+    args = {"--string", "mystring"};
+    EXPECT_THROW(run(), CLI::ArgumentMismatch);
+
+    args = {"--string", "mystring", "mystring2", "string2", "--string", "string4", "string5"};
+    EXPECT_THROW(run(), CLI::ArgumentMismatch);
+
+    EXPECT_EQ(opt->get_expected_max(), 4);
+    EXPECT_EQ(opt->get_expected_min(), 2);
+    opt->expected(4, 2); // just test the handling of reversed arguments
+    EXPECT_EQ(opt->get_expected_max(), 4);
+    EXPECT_EQ(opt->get_expected_min(), 2);
+    opt->expected(-5);
+    EXPECT_EQ(opt->get_expected_max(), 5);
+    EXPECT_EQ(opt->get_expected_min(), 5);
+    opt->expected(-5, 7);
+    EXPECT_EQ(opt->get_expected_max(), 7);
+    EXPECT_EQ(opt->get_expected_min(), 5);
+}
+
 TEST_F(TApp, VectorFancyOpts) {
     std::vector<std::string> strvec;
     std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
@@ -1925,7 +2116,7 @@ TEST_F(TApp, Env) {
 
     put_env("CLI11_TEST_ENV_TMP", "2");
 
-    int val = 1;
+    int val{1};
     CLI::Option *vopt = app.add_option("--tmp", val)->envname("CLI11_TEST_ENV_TMP");
 
     run();
@@ -1941,7 +2132,7 @@ TEST_F(TApp, Env) {
 }
 
 TEST_F(TApp, RangeInt) {
-    int x = 0;
+    int x{0};
     app.add_option("--one", x)->check(CLI::Range(3, 6));
 
     args = {"--one=1"};
@@ -1962,7 +2153,7 @@ TEST_F(TApp, RangeInt) {
 
 TEST_F(TApp, RangeDouble) {
 
-    double x = 0;
+    double x{0.0};
     /// Note that this must be a double in Range, too
     app.add_option("--one", x)->check(CLI::Range(3.0, 6.0));
 
@@ -1987,7 +2178,7 @@ TEST_F(TApp, AllowExtras) {
 
     app.allow_extras();
 
-    bool val = true;
+    bool val{true};
     app.add_flag("-f", val);
 
     args = {"-x", "-f"};
@@ -2047,8 +2238,8 @@ TEST_F(TApp, AllowExtrasCascadeDirect) {
     EXPECT_EQ(app.remaining(), std::vector<std::string>({"-x", "45", "-f", "27"}));
 
     CLI::App capp{"cascade_program"};
-    int v1 = 0;
-    int v2 = 0;
+    int v1{0};
+    int v2{0};
     capp.add_option("-x", v1);
     capp.add_option("-f", v2);
 
@@ -2059,8 +2250,8 @@ TEST_F(TApp, AllowExtrasCascadeDirect) {
 
 TEST_F(TApp, AllowExtrasArgModify) {
 
-    int v1 = 0;
-    int v2 = 0;
+    int v1{0};
+    int v2{0};
     app.allow_extras();
     app.add_option("-f", v2);
     args = {"27", "-f", "45", "-x"};
@@ -2138,7 +2329,7 @@ TEST_F(TApp, FallthroughParents) {
 }
 
 TEST_F(TApp, OptionWithDefaults) {
-    int someint = 2;
+    int someint{2};
     app.add_option("-a", someint)->capture_default_str();
 
     args = {"-a1", "-a2"};
@@ -2227,6 +2418,138 @@ TEST_F(TApp, CustomDoubleOptionAlt) {
     EXPECT_DOUBLE_EQ(custom_opt.second, 1.5);
 }
 
+// now with independent type sizes and expected this is possible
+TEST_F(TApp, vectorPair) {
+
+    std::vector<std::pair<int, std::string>> custom_opt;
+
+    auto opt = app.add_option("--dict", custom_opt);
+
+    args = {"--dict", "1", "str1", "--dict", "3", "str3"};
+
+    run();
+    EXPECT_EQ(custom_opt.size(), 2u);
+    EXPECT_EQ(custom_opt[0].first, 1);
+    EXPECT_EQ(custom_opt[1].second, "str3");
+
+    args = {"--dict", "1", "str1", "--dict", "3", "str3", "--dict", "-1", "str4"};
+    run();
+    EXPECT_EQ(custom_opt.size(), 3u);
+    EXPECT_EQ(custom_opt[2].first, -1);
+    EXPECT_EQ(custom_opt[2].second, "str4");
+    opt->check(CLI::PositiveNumber.application_index(0));
+
+    EXPECT_THROW(run(), CLI::ValidationError);
+}
+
+TEST_F(TApp, vectorPairFail) {
+
+    std::vector<std::pair<int, std::string>> custom_opt;
+
+    app.add_option("--dict", custom_opt);
+
+    args = {"--dict", "1", "str1", "--dict", "str3", "1"};
+
+    EXPECT_THROW(run(), CLI::ConversionError);
+}
+
+TEST_F(TApp, vectorPairTypeRange) {
+
+    std::vector<std::pair<int, std::string>> custom_opt;
+
+    auto opt = app.add_option("--dict", custom_opt);
+
+    opt->type_size(2, 1); // just test switched arguments
+    EXPECT_EQ(opt->get_type_size_min(), 1);
+    EXPECT_EQ(opt->get_type_size_max(), 2);
+
+    args = {"--dict", "1", "str1", "--dict", "3", "str3"};
+
+    run();
+    EXPECT_EQ(custom_opt.size(), 2u);
+    EXPECT_EQ(custom_opt[0].first, 1);
+    EXPECT_EQ(custom_opt[1].second, "str3");
+
+    args = {"--dict", "1", "str1", "--dict", "3", "--dict", "-1", "str4"};
+    run();
+    EXPECT_EQ(custom_opt.size(), 3u);
+    EXPECT_TRUE(custom_opt[1].second.empty());
+    EXPECT_EQ(custom_opt[2].first, -1);
+    EXPECT_EQ(custom_opt[2].second, "str4");
+
+    opt->type_size(-2, -1); // test negative arguments
+    EXPECT_EQ(opt->get_type_size_min(), 1);
+    EXPECT_EQ(opt->get_type_size_max(), 2);
+    // this type size spec should run exactly as before
+    run();
+    EXPECT_EQ(custom_opt.size(), 3u);
+    EXPECT_TRUE(custom_opt[1].second.empty());
+    EXPECT_EQ(custom_opt[2].first, -1);
+    EXPECT_EQ(custom_opt[2].second, "str4");
+}
+
+// now with independent type sizes and expected this is possible
+TEST_F(TApp, vectorTuple) {
+
+    std::vector<std::tuple<int, std::string, double>> custom_opt;
+
+    auto opt = app.add_option("--dict", custom_opt);
+
+    args = {"--dict", "1", "str1", "4.3", "--dict", "3", "str3", "2.7"};
+
+    run();
+    EXPECT_EQ(custom_opt.size(), 2u);
+    EXPECT_EQ(std::get<0>(custom_opt[0]), 1);
+    EXPECT_EQ(std::get<1>(custom_opt[1]), "str3");
+    EXPECT_EQ(std::get<2>(custom_opt[1]), 2.7);
+
+    args = {"--dict", "1", "str1", "4.3", "--dict", "3", "str3", "2.7", "--dict", "-1", "str4", "-1.87"};
+    run();
+    EXPECT_EQ(custom_opt.size(), 3u);
+    EXPECT_EQ(std::get<0>(custom_opt[2]), -1);
+    EXPECT_EQ(std::get<1>(custom_opt[2]), "str4");
+    EXPECT_EQ(std::get<2>(custom_opt[2]), -1.87);
+    opt->check(CLI::PositiveNumber.application_index(0));
+
+    EXPECT_THROW(run(), CLI::ValidationError);
+
+    args.back() = "haha";
+    args[9] = "45";
+    EXPECT_THROW(run(), CLI::ConversionError);
+}
+
+// now with independent type sizes and expected this is possible
+TEST_F(TApp, vectorVector) {
+
+    std::vector<std::vector<int>> custom_opt;
+
+    auto opt = app.add_option("--dict", custom_opt);
+
+    args = {"--dict", "1", "2", "4", "--dict", "3", "1"};
+
+    run();
+    EXPECT_EQ(custom_opt.size(), 2u);
+    EXPECT_EQ(custom_opt[0].size(), 3u);
+    EXPECT_EQ(custom_opt[1].size(), 2u);
+
+    args = {"--dict", "1", "2", "4", "--dict", "3", "1", "--dict", "3", "--dict",
+            "3",      "3", "3", "3", "3",      "3", "3", "3",      "3", "-3"};
+    run();
+    EXPECT_EQ(custom_opt.size(), 4u);
+    EXPECT_EQ(custom_opt[0].size(), 3u);
+    EXPECT_EQ(custom_opt[1].size(), 2u);
+    EXPECT_EQ(custom_opt[2].size(), 1u);
+    EXPECT_EQ(custom_opt[3].size(), 10u);
+    opt->check(CLI::PositiveNumber.application_index(9));
+
+    EXPECT_THROW(run(), CLI::ValidationError);
+    args.pop_back();
+    EXPECT_NO_THROW(run());
+
+    args.back() = "haha";
+    EXPECT_THROW(run(), CLI::ConversionError);
+}
+
 // #128
 TEST_F(TApp, RepeatingMultiArgumentOptions) {
     std::vector<std::string> entries;
@@ -2261,8 +2584,8 @@ TEST_F(TApp, EmptyOptionFail) {
 }
 
 TEST_F(TApp, BeforeRequirements) {
-    app.add_flag_function("-a", [](int64_t) { throw CLI::Success(); });
-    app.add_flag_function("-b", [](int64_t) { throw CLI::CallForHelp(); });
+    app.add_flag_function("-a", [](std::int64_t) { throw CLI::Success(); });
+    app.add_flag_function("-b", [](std::int64_t) { throw CLI::CallForHelp(); });
 
     args = {"extra"};
     EXPECT_THROW(run(), CLI::ExtrasError);
@@ -2285,7 +2608,7 @@ TEST_F(TApp, BeforeRequirements) {
 // #209
 TEST_F(TApp, CustomUserSepParse) {
 
-    std::vector<int> vals = {1, 2, 3};
+    std::vector<int> vals{1, 2, 3};
     args = {"--idx", "1,2,3"};
     auto opt = app.add_option("--idx", vals)->delimiter(',');
     run();
@@ -2329,7 +2652,7 @@ TEST_F(TApp, BadUserSepParse) {
 // #209
 TEST_F(TApp, CustomUserSepParse2) {
 
-    std::vector<int> vals = {1, 2, 3};
+    std::vector<int> vals{1, 2, 3};
     args = {"--idx", "1,2,"};
     auto opt = app.add_option("--idx", vals)->delimiter(',');
     run();
@@ -2344,7 +2667,7 @@ TEST_F(TApp, CustomUserSepParse2) {
 
 TEST_F(TApp, CustomUserSepParseFunction) {
 
-    std::vector<int> vals = {1, 2, 3};
+    std::vector<int> vals{1, 2, 3};
     args = {"--idx", "1,2,3"};
     app.add_option_function<std::vector<int>>("--idx", [&vals](std::vector<int> v) { vals = std::move(v); })
         ->delimiter(',');
diff --git a/packages/CLI11/tests/CMakeLists.txt b/packages/CLI11/tests/CMakeLists.txt
index 9e021f7451f1582fb7603b8ecfe748c8407350ea..f697867d7c5b1985f0aaaf3145d7fe0babb4beb2 100644
--- a/packages/CLI11/tests/CMakeLists.txt
+++ b/packages/CLI11/tests/CMakeLists.txt
@@ -5,10 +5,21 @@ endif()
 
 list(APPEND CMAKE_MODULE_PATH "${CLI11_SOURCE_DIR}/cmake")
 
-# If submodule is available, add sanitizers
-# Set SANITIZE_ADDRESS, SANITIZE_MEMORY, SANITIZE_THREAD or SANITIZE_UNDEFINED
-if(EXISTS "${CLI11_SOURCE_DIR}/extern/sanitizers/cmake/FindSanitizers.cmake")
-    list(APPEND CMAKE_MODULE_PATH "${CLI11_SOURCE_DIR}/extern/sanitizers/cmake")
+if(CLI11_SANITIZERS)
+    message(STATUS "Using arsenm/sanitizers-cmake")
+    FetchContent_Declare(sanitizers
+        GIT_REPOSITORY https://github.com/arsenm/sanitizers-cmake.git
+        GIT_SHALLOW 1
+        GIT_TAG 99e159e)
+
+    FetchContent_GetProperties(sanitizers)
+
+    if (NOT sanitizers_POPULATED)
+        FetchContent_Populate(sanitizers)
+    endif()
+
+    list(APPEND CMAKE_MODULE_PATH "${sanitizers_SOURCE_DIR}/cmake")
+
     find_package(Sanitizers)
     if(SANITIZE_ADDRESS)
         message(STATUS "You might want to use \"${ASan_WRAPPER}\" to run your program")
@@ -23,7 +34,7 @@ include(AddGoogletest)
 
 set(CLI11_TESTS
     HelpersTest
-    IniTest
+    ConfigFileTest
     SimpleTest
     AppTest
     SetTest
@@ -44,51 +55,53 @@ if(WIN32)
     list(APPEND CLI11_TESTS WindowsTest)
 endif()
 
-set(CLI11_MULTIONLY_TESTS
-    TimerTest
-    )
+set(CLI11_MULTIONLY_TESTS TimerTest)
 
 # Only affects current directory, so safe
 include_directories(${CMAKE_CURRENT_SOURCE_DIR})
 
-foreach(T ${CLI11_TESTS})
-
+foreach(T IN LISTS CLI11_TESTS)
+    if(CLI11_CUDA_TESTS)
+        set_property(
+            SOURCE ${T}.cpp
+            PROPERTY LANGUAGE CUDA
+            )
+    endif()
     add_executable(${T} ${T}.cpp ${CLI11_headers})
     add_sanitizers(${T})
-    target_link_libraries(${T} PUBLIC CLI11 CLI11_warnings)
+    if(NOT CLI11_CUDA_TESTS)
+        target_link_libraries(${T} PRIVATE CLI11_warnings)
+    endif()
+    target_link_libraries(${T} PRIVATE CLI11)
     add_gtest(${T})
 
     if(CLI11_SINGLE_FILE AND CLI11_SINGLE_FILE_TESTS)
         add_executable(${T}_Single ${T}.cpp)
-        target_link_libraries(${T}_Single PUBLIC CLI11_SINGLE)
+        target_link_libraries(${T}_Single PRIVATE CLI11_SINGLE)
         add_gtest(${T}_Single)
-        set_target_properties(${T}_Single
-            PROPERTIES
-            FOLDER "Tests Single File")
+        set_property(TARGET ${T}_Single PROPERTY FOLDER "Tests Single File")
     endif()
-
 endforeach()
 
-foreach(T ${CLI11_MULTIONLY_TESTS})
-
+foreach(T IN LISTS CLI11_MULTIONLY_TESTS)
     add_executable(${T} ${T}.cpp ${CLI11_headers})
     add_sanitizers(${T})
     target_link_libraries(${T} PUBLIC CLI11)
     add_gtest(${T})
-
 endforeach()
 
 # Add -Wno-deprecated-declarations to DeprecatedTest
-if(NOT MSVC)
-    target_compile_options(DeprecatedTest PRIVATE -Wno-deprecated-declarations)
-    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()
+set(no-deprecated-declarations
+    $<$<CXX_COMPILER_ID:MSVC>:/wd4996>
+    $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wno-deprecated-declarations>
+    )
+target_compile_options(DeprecatedTest
+    PRIVATE
+    ${no-deprecated-declarations})
+if (TARGET DeprecatedTest_Single)
+    target_compile_options(DeprecatedTest_Single
+        PRIVATE
+        ${no-deprecated-declarations})
 endif()
 
 # Link test (build error if inlines missing)
@@ -98,11 +111,22 @@ set_target_properties(link_test_1 PROPERTIES FOLDER "Tests")
 add_executable(link_test_2 link_test_2.cpp)
 target_link_libraries(link_test_2 PUBLIC CLI11 link_test_1)
 add_gtest(link_test_2)
+if(CLI11_FORCE_LIBCXX)
+    set_property(TARGET link_test_1 APPEND_STRING
+        PROPERTY LINK_FLAGS -stdlib=libc++)
+    set_property(TARGET link_test_2 APPEND_STRING
+        PROPERTY LINK_FLAGS -stdlib=libc++)
+endif()
 
 # Add informational printout
-# Force this to be in a standard location so CTest can find it
 add_executable(informational informational.cpp)
 target_link_libraries(informational PUBLIC CLI11)
+if(CLI11_FORCE_LIBCXX)
+    set_property(TARGET informational APPEND_STRING
+        PROPERTY LINK_FLAGS -stdlib=libc++)
+endif()
+
+# Force this to be in a standard location so CTest can find it
 set_target_properties(informational PROPERTIES
     RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
     RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}"
@@ -114,21 +138,22 @@ set_target_properties(informational PROPERTIES
 # Adding this printout to CTest
 file(WRITE "${PROJECT_BINARY_DIR}/CTestCustom.cmake"
     "set(CTEST_CUSTOM_PRE_TEST \"${CMAKE_BINARY_DIR}/informational\")"
-    )
+)
 
 # Add boost to test boost::optional if available
 find_package(Boost 1.61)
-if(Boost_FOUND)
-    if(TARGET Boost::boost)
-        target_link_libraries(informational PRIVATE Boost::boost)
-        target_link_libraries(OptionalTest PRIVATE Boost::boost)
-    else()
-        target_include_directories(informational PRIVATE ${Boost_INCLUDE_DIRS})
-        target_include_directories(OptionalTest PRIVATE ${Boost_INCLUDE_DIRS})
-    endif()
 
-    target_compile_definitions(informational PRIVATE CLI11_BOOST_OPTIONAL)
-    target_compile_definitions(OptionalTest PRIVATE CLI11_BOOST_OPTIONAL)
+set(boost-optional-def $<$<BOOL:${Boost_FOUND}>:CLI11_BOOST_OPTIONAL>)
+
+target_compile_definitions(informational PRIVATE ${boost-optional-def})
+target_compile_definitions(OptionalTest PRIVATE ${boost-optional-def})
+
+if(TARGET Boost::boost)
+    target_link_libraries(informational PRIVATE Boost::boost)
+    target_link_libraries(OptionalTest PRIVATE Boost::boost)
+elseif(BOOST_FOUND)
+    target_include_directories(informational PRIVATE ${Boost_INCLUDE_DIRS})
+    target_include_directories(OptionalTest PRIVATE ${Boost_INCLUDE_DIRS})
 endif()
 
 if(CMAKE_BUILD_TYPE STREQUAL Coverage)
@@ -138,6 +163,7 @@ if(CMAKE_BUILD_TYPE STREQUAL Coverage)
         EXECUTABLE ctest
         DEPENDENCIES
           ${CLI11_TESTS}
-          ${CLI11_MULTIONLY_TESTS})
+          ${CLI11_MULTIONLY_TESTS}
+        )
 endif()
 
diff --git a/packages/CLI11/tests/ConfigFileTest.cpp b/packages/CLI11/tests/ConfigFileTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..855588557ff9fd5811cc7a02adb427a9138a89c0
--- /dev/null
+++ b/packages/CLI11/tests/ConfigFileTest.cpp
@@ -0,0 +1,1757 @@
+#include "app_helper.hpp"
+
+#include "gmock/gmock.h"
+#include <cstdio>
+#include <sstream>
+
+using ::testing::HasSubstr;
+using ::testing::Not;
+
+TEST(StringBased, convert_arg_for_ini) {
+
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini(std::string{}), "\"\"");
+
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("true"), "true");
+
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("nan"), "nan");
+
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("happy hippo"), "\"happy hippo\"");
+
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("47"), "47");
+
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("47.365225"), "47.365225");
+
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("+3.28e-25"), "+3.28e-25");
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("-22E14"), "-22E14");
+
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("a"), "'a'");
+    // hex
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("0x5461FAED"), "0x5461FAED");
+    // hex fail
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("0x5461FAEG"), "\"0x5461FAEG\"");
+
+    // octal
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("0o546123567"), "0o546123567");
+    // octal fail
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("0o546123587"), "\"0o546123587\"");
+
+    // binary
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("0b01101110010"), "0b01101110010");
+    // binary fail
+    EXPECT_EQ(CLI::detail::convert_arg_for_ini("0b01102110010"), "\"0b01102110010\"");
+}
+
+TEST(StringBased, IniJoin) {
+    std::vector<std::string> items = {"one", "two", "three four"};
+    std::string result = "\"one\" \"two\" \"three four\"";
+
+    EXPECT_EQ(CLI::detail::ini_join(items, ' ', '\0', '\0'), result);
+
+    result = "[\"one\", \"two\", \"three four\"]";
+
+    EXPECT_EQ(CLI::detail::ini_join(items), result);
+
+    result = "{\"one\"; \"two\"; \"three four\"}";
+
+    EXPECT_EQ(CLI::detail::ini_join(items, ';', '{', '}'), result);
+}
+
+TEST(StringBased, First) {
+    std::stringstream ofile;
+
+    ofile << "one=three\n";
+    ofile << "two=four\n";
+
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    EXPECT_EQ(2u, output.size());
+    EXPECT_EQ("one", output.at(0).name);
+    EXPECT_EQ(1u, output.at(0).inputs.size());
+    EXPECT_EQ("three", output.at(0).inputs.at(0));
+    EXPECT_EQ("two", output.at(1).name);
+    EXPECT_EQ(1u, output.at(1).inputs.size());
+    EXPECT_EQ("four", output.at(1).inputs.at(0));
+}
+
+TEST(StringBased, FirstWithComments) {
+    std::stringstream ofile;
+
+    ofile << ";this is a comment\n";
+    ofile << "one=three\n";
+    ofile << "two=four\n";
+    ofile << "; and another one\n";
+
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    EXPECT_EQ(2u, output.size());
+    EXPECT_EQ("one", output.at(0).name);
+    EXPECT_EQ(1u, output.at(0).inputs.size());
+    EXPECT_EQ("three", output.at(0).inputs.at(0));
+    EXPECT_EQ("two", output.at(1).name);
+    EXPECT_EQ(1u, output.at(1).inputs.size());
+    EXPECT_EQ("four", output.at(1).inputs.at(0));
+}
+
+TEST(StringBased, Quotes) {
+    std::stringstream ofile;
+
+    ofile << R"(one = "three")" << '\n';
+    ofile << R"(two = 'four')" << '\n';
+    ofile << R"(five = "six and seven")" << '\n';
+
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    EXPECT_EQ(3u, output.size());
+    EXPECT_EQ("one", output.at(0).name);
+    EXPECT_EQ(1u, output.at(0).inputs.size());
+    EXPECT_EQ("three", output.at(0).inputs.at(0));
+    EXPECT_EQ("two", output.at(1).name);
+    EXPECT_EQ(1u, output.at(1).inputs.size());
+    EXPECT_EQ("four", output.at(1).inputs.at(0));
+    EXPECT_EQ("five", output.at(2).name);
+    EXPECT_EQ(1u, output.at(2).inputs.size());
+    EXPECT_EQ("six and seven", output.at(2).inputs.at(0));
+}
+
+TEST(StringBased, Vector) {
+    std::stringstream ofile;
+
+    ofile << "one = three\n";
+    ofile << "two = four\n";
+    ofile << "five = six and seven\n";
+
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    EXPECT_EQ(3u, output.size());
+    EXPECT_EQ("one", output.at(0).name);
+    EXPECT_EQ(1u, output.at(0).inputs.size());
+    EXPECT_EQ("three", output.at(0).inputs.at(0));
+    EXPECT_EQ("two", output.at(1).name);
+    EXPECT_EQ(1u, output.at(1).inputs.size());
+    EXPECT_EQ("four", output.at(1).inputs.at(0));
+    EXPECT_EQ("five", output.at(2).name);
+    EXPECT_EQ(3u, output.at(2).inputs.size());
+    EXPECT_EQ("six", output.at(2).inputs.at(0));
+    EXPECT_EQ("and", output.at(2).inputs.at(1));
+    EXPECT_EQ("seven", output.at(2).inputs.at(2));
+}
+
+TEST(StringBased, Spaces) {
+    std::stringstream ofile;
+
+    ofile << "one = three\n";
+    ofile << "two = four";
+
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    EXPECT_EQ(2u, output.size());
+    EXPECT_EQ("one", output.at(0).name);
+    EXPECT_EQ(1u, output.at(0).inputs.size());
+    EXPECT_EQ("three", output.at(0).inputs.at(0));
+    EXPECT_EQ("two", output.at(1).name);
+    EXPECT_EQ(1u, output.at(1).inputs.size());
+    EXPECT_EQ("four", output.at(1).inputs.at(0));
+}
+
+TEST(StringBased, Sections) {
+    std::stringstream ofile;
+
+    ofile << "one=three\n";
+    ofile << "[second]\n";
+    ofile << "  two=four\n";
+
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    EXPECT_EQ(4u, output.size());
+    EXPECT_EQ("one", output.at(0).name);
+    EXPECT_EQ(1u, output.at(0).inputs.size());
+    EXPECT_EQ("three", output.at(0).inputs.at(0));
+    EXPECT_EQ("two", output.at(2).name);
+    EXPECT_EQ("second", output.at(2).parents.at(0));
+    EXPECT_EQ(1u, output.at(2).inputs.size());
+    EXPECT_EQ("four", output.at(2).inputs.at(0));
+    EXPECT_EQ("second.two", output.at(2).fullname());
+}
+
+TEST(StringBased, SpacesSections) {
+    std::stringstream ofile;
+
+    ofile << "one=three\n\n";
+    ofile << "[second]   \n";
+    ofile << "   \n";
+    ofile << "  two=four\n";
+
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    EXPECT_EQ(4u, output.size());
+    EXPECT_EQ("one", output.at(0).name);
+    EXPECT_EQ(1u, output.at(0).inputs.size());
+    EXPECT_EQ("three", output.at(0).inputs.at(0));
+    EXPECT_EQ("second", output.at(1).parents.at(0));
+    EXPECT_EQ("++", output.at(1).name);
+    EXPECT_EQ("two", output.at(2).name);
+    EXPECT_EQ(1u, output.at(2).parents.size());
+    EXPECT_EQ("second", output.at(2).parents.at(0));
+    EXPECT_EQ(1u, output.at(2).inputs.size());
+    EXPECT_EQ("four", output.at(2).inputs.at(0));
+    EXPECT_EQ("second", output.at(3).parents.at(0));
+    EXPECT_EQ("--", output.at(3).name);
+}
+
+// check function to make sure that open sections match close sections
+bool checkSections(const std::vector<CLI::ConfigItem> &output) {
+    std::set<std::string> open;
+    for(auto &ci : output) {
+        if(ci.name == "++") {
+            auto nm = ci.fullname();
+            nm.pop_back();
+            nm.pop_back();
+            auto rv = open.insert(nm);
+            if(!rv.second) {
+                return false;
+            }
+        }
+        if(ci.name == "--") {
+            auto nm = ci.fullname();
+            nm.pop_back();
+            nm.pop_back();
+            auto rv = open.erase(nm);
+            if(rv != 1U) {
+                return false;
+            }
+        }
+    }
+    return open.empty();
+}
+TEST(StringBased, Layers) {
+    std::stringstream ofile;
+
+    ofile << "simple = true\n\n";
+    ofile << "[other]\n";
+    ofile << "[other.sub2]\n";
+    ofile << "[other.sub2.sub-level2]\n";
+    ofile << "[other.sub2.sub-level2.sub-level3]\n";
+    ofile << "absolute_newest = true\n";
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    // 2 flags and 4 openings and 4 closings
+    EXPECT_EQ(10u, output.size());
+    EXPECT_TRUE(checkSections(output));
+}
+
+TEST(StringBased, LayersSkip) {
+    std::stringstream ofile;
+
+    ofile << "simple = true\n\n";
+    ofile << "[other.sub2]\n";
+    ofile << "[other.sub2.sub-level2.sub-level3]\n";
+    ofile << "absolute_newest = true\n";
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    // 2 flags and 4 openings and 4 closings
+    EXPECT_EQ(10u, output.size());
+    EXPECT_TRUE(checkSections(output));
+}
+
+TEST(StringBased, LayersSkipOrdered) {
+    std::stringstream ofile;
+
+    ofile << "simple = true\n\n";
+    ofile << "[other.sub2.sub-level2.sub-level3]\n";
+    ofile << "[other.sub2]\n";
+    ofile << "absolute_newest = true\n";
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    // 2 flags and 4 openings and 4 closings
+    EXPECT_EQ(12u, output.size());
+    EXPECT_TRUE(checkSections(output));
+}
+
+TEST(StringBased, LayersChange) {
+    std::stringstream ofile;
+
+    ofile << "simple = true\n\n";
+    ofile << "[other.sub2]\n";
+    ofile << "[other.sub3]\n";
+    ofile << "absolute_newest = true\n";
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    // 2 flags and 3 openings and 3 closings
+    EXPECT_EQ(8u, output.size());
+    EXPECT_TRUE(checkSections(output));
+}
+
+TEST(StringBased, Layers2LevelChange) {
+    std::stringstream ofile;
+
+    ofile << "simple = true\n\n";
+    ofile << "[other.sub2.cmd]\n";
+    ofile << "[other.sub3.cmd]\n";
+    ofile << "absolute_newest = true\n";
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    // 2 flags and 5 openings and 5 closings
+    EXPECT_EQ(12u, output.size());
+    EXPECT_TRUE(checkSections(output));
+}
+
+TEST(StringBased, Layers3LevelChange) {
+    std::stringstream ofile;
+
+    ofile << "[other.sub2.subsub.cmd]\n";
+    ofile << "[other.sub3.subsub.cmd]\n";
+    ofile << "absolute_newest = true\n";
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    // 1 flags and 7 openings and 7 closings
+    EXPECT_EQ(15u, output.size());
+    EXPECT_TRUE(checkSections(output));
+}
+
+TEST(StringBased, newSegment) {
+    std::stringstream ofile;
+
+    ofile << "[other.sub2.subsub.cmd]\n";
+    ofile << "flag = true\n";
+    ofile << "[another]\n";
+    ofile << "absolute_newest = true\n";
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    // 2 flags and 5 openings and 5 closings
+    EXPECT_EQ(12u, output.size());
+    EXPECT_TRUE(checkSections(output));
+}
+
+TEST(StringBased, LayersDirect) {
+    std::stringstream ofile;
+
+    ofile << "simple = true\n\n";
+    ofile << "[other.sub2.sub-level2.sub-level3]\n";
+    ofile << "absolute_newest = true\n";
+
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    // 2 flags and 4 openings and 4 closings
+    EXPECT_EQ(10u, output.size());
+    EXPECT_TRUE(checkSections(output));
+}
+
+TEST(StringBased, LayersComplex) {
+    std::stringstream ofile;
+
+    ofile << "simple = true\n\n";
+    ofile << "[other.sub2.sub-level2.sub-level3]\n";
+    ofile << "absolute_newest = true\n";
+    ofile << "[other.sub2.sub-level2]\n";
+    ofile << "still_newer = true\n";
+    ofile << "[other.sub2]\n";
+    ofile << "newest = true\n";
+
+    ofile.seekg(0, std::ios::beg);
+
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
+
+    // 4 flags and 6 openings and 6 closings
+    EXPECT_EQ(16u, output.size());
+    EXPECT_TRUE(checkSections(output));
+}
+
+TEST(StringBased, file_error) {
+    EXPECT_THROW(std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_file("nonexist_file"), CLI::FileError);
+}
+
+TEST_F(TApp, IniNotRequired) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=99" << std::endl;
+        out << "three=3" << std::endl;
+    }
+
+    int one = 0, two = 0, three = 0;
+    app.add_option("--one", one);
+    app.add_option("--two", two);
+    app.add_option("--three", three);
+
+    args = {"--one=1"};
+
+    run();
+
+    EXPECT_EQ(1, one);
+    EXPECT_EQ(99, two);
+    EXPECT_EQ(3, three);
+
+    one = two = three = 0;
+    args = {"--one=1", "--two=2"};
+
+    run();
+
+    EXPECT_EQ(1, one);
+    EXPECT_EQ(2, two);
+    EXPECT_EQ(3, three);
+    EXPECT_EQ(app["--config"]->as<std::string>(), "TestIniTmp.ini");
+}
+
+TEST_F(TApp, IniSuccessOnUnknownOption) {
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+    app.allow_config_extras(true);
+
+    {
+        std::ofstream out{tmpini};
+        out << "three=3" << std::endl;
+        out << "two=99" << std::endl;
+    }
+
+    int two{0};
+    app.add_option("--two", two);
+    run();
+    EXPECT_EQ(99, two);
+}
+
+TEST_F(TApp, IniGetRemainingOption) {
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+    app.allow_config_extras(true);
+
+    std::string ExtraOption = "three";
+    std::string ExtraOptionValue = "3";
+    {
+        std::ofstream out{tmpini};
+        out << ExtraOption << "=" << ExtraOptionValue << std::endl;
+        out << "two=99" << std::endl;
+    }
+
+    int two{0};
+    app.add_option("--two", two);
+    ASSERT_NO_THROW(run());
+    std::vector<std::string> ExpectedRemaining = {ExtraOption};
+    EXPECT_EQ(app.remaining(), ExpectedRemaining);
+}
+
+TEST_F(TApp, IniGetNoRemaining) {
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+    app.allow_config_extras(true);
+
+    {
+        std::ofstream out{tmpini};
+        out << "two=99" << std::endl;
+    }
+
+    int two{0};
+    app.add_option("--two", two);
+    ASSERT_NO_THROW(run());
+    EXPECT_EQ(app.remaining().size(), 0u);
+}
+
+TEST_F(TApp, IniRequiredNoDefault) {
+
+    app.set_config("--config")->required();
+
+    int two{0};
+    app.add_option("--two", two);
+    ASSERT_THROW(run(), CLI::FileError);
+}
+
+TEST_F(TApp, IniNotRequiredNoDefault) {
+
+    app.set_config("--config");
+
+    int two{0};
+    app.add_option("--two", two);
+    ASSERT_NO_THROW(run());
+}
+
+/// Define a class for testing purposes that does bad things
+class EvilConfig : public CLI::Config {
+  public:
+    EvilConfig() = default;
+    virtual std::string to_config(const CLI::App *, bool, bool, std::string) const { throw CLI::FileError("evil"); }
+
+    virtual std::vector<CLI::ConfigItem> from_config(std::istream &) const { throw CLI::FileError("evil"); }
+};
+
+TEST_F(TApp, IniRequiredbadConfigurator) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=99" << std::endl;
+        out << "three=3" << std::endl;
+    }
+
+    app.set_config("--config", tmpini)->required();
+    app.config_formatter(std::make_shared<EvilConfig>());
+    int two{0};
+    app.add_option("--two", two);
+    ASSERT_THROW(run(), CLI::FileError);
+}
+
+TEST_F(TApp, IniNotRequiredbadConfigurator) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=99" << std::endl;
+        out << "three=3" << std::endl;
+    }
+
+    app.set_config("--config", tmpini);
+    app.config_formatter(std::make_shared<EvilConfig>());
+    int two{0};
+    app.add_option("--two", two);
+    ASSERT_NO_THROW(run());
+}
+
+TEST_F(TApp, IniNotRequiredNotDefault) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+    TempFile tmpini2{"TestIniTmp2.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=99" << std::endl;
+        out << "three=3" << std::endl;
+    }
+
+    {
+        std::ofstream out{tmpini2};
+        out << "[default]" << std::endl;
+        out << "two=98" << std::endl;
+        out << "three=4" << std::endl;
+    }
+
+    int one{0}, two{0}, three{0};
+    app.add_option("--one", one);
+    app.add_option("--two", two);
+    app.add_option("--three", three);
+
+    run();
+    EXPECT_EQ(app["--config"]->as<std::string>(), tmpini.c_str());
+    EXPECT_EQ(99, two);
+    EXPECT_EQ(3, three);
+
+    args = {"--config", tmpini2};
+    run();
+
+    EXPECT_EQ(98, two);
+    EXPECT_EQ(4, three);
+    EXPECT_EQ(app.get_config_ptr()->as<std::string>(), tmpini2.c_str());
+}
+
+TEST_F(TApp, IniRequiredNotFound) {
+
+    std::string noini = "TestIniNotExist.ini";
+    app.set_config("--config", noini, "", true);
+
+    EXPECT_THROW(run(), CLI::FileError);
+}
+
+TEST_F(TApp, IniNotRequiredPassedNotFound) {
+
+    std::string noini = "TestIniNotExist.ini";
+    app.set_config("--config", "", "", false);
+
+    args = {"--config", noini};
+    EXPECT_THROW(run(), CLI::FileError);
+}
+
+TEST_F(TApp, IniOverwrite) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=99" << std::endl;
+    }
+
+    std::string orig = "filename_not_exist.ini";
+    std::string next = "TestIniTmp.ini";
+    app.set_config("--config", orig);
+    // Make sure this can be overwritten
+    app.set_config("--conf", next);
+    int two{7};
+    app.add_option("--two", two);
+
+    run();
+
+    EXPECT_EQ(99, two);
+}
+
+TEST_F(TApp, IniRequired) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini, "", true);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=99" << std::endl;
+        out << "three=3" << std::endl;
+    }
+
+    int one{0}, two{0}, three{0};
+    app.add_option("--one", one)->required();
+    app.add_option("--two", two)->required();
+    app.add_option("--three", three)->required();
+
+    args = {"--one=1"};
+
+    run();
+    EXPECT_EQ(one, 1);
+    EXPECT_EQ(two, 99);
+    EXPECT_EQ(three, 3);
+
+    one = two = three = 0;
+    args = {"--one=1", "--two=2"};
+
+    EXPECT_NO_THROW(run());
+    EXPECT_EQ(one, 1);
+    EXPECT_EQ(two, 2);
+    EXPECT_EQ(three, 3);
+
+    args = {};
+
+    EXPECT_THROW(run(), CLI::RequiredError);
+
+    args = {"--two=2"};
+
+    EXPECT_THROW(run(), CLI::RequiredError);
+}
+
+TEST_F(TApp, IniVector) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=2 3" << std::endl;
+        out << "three=1 2 3" << std::endl;
+    }
+
+    std::vector<int> two, three;
+    app.add_option("--two", two)->expected(2)->required();
+    app.add_option("--three", three)->required();
+
+    run();
+
+    EXPECT_EQ(std::vector<int>({2, 3}), two);
+    EXPECT_EQ(std::vector<int>({1, 2, 3}), three);
+}
+TEST_F(TApp, TOMLVector) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "#this is a comment line\n";
+        out << "[default]\n";
+        out << "two=[2,3]\n";
+        out << "three=[1,2,3]\n";
+    }
+
+    std::vector<int> two, three;
+    app.add_option("--two", two)->expected(2)->required();
+    app.add_option("--three", three)->required();
+
+    run();
+
+    EXPECT_EQ(std::vector<int>({2, 3}), two);
+    EXPECT_EQ(std::vector<int>({1, 2, 3}), three);
+}
+
+TEST_F(TApp, ColonValueSep) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "#this is a comment line\n";
+        out << "[default]\n";
+        out << "two:2\n";
+        out << "three:3\n";
+    }
+
+    int two{0}, three{0};
+    app.add_option("--two", two);
+    app.add_option("--three", three);
+
+    app.get_config_formatter_base()->valueSeparator(':');
+
+    run();
+
+    EXPECT_EQ(2, two);
+    EXPECT_EQ(3, three);
+}
+
+TEST_F(TApp, TOMLVectordirect) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    app.config_formatter(std::make_shared<CLI::ConfigTOML>());
+
+    {
+        std::ofstream out{tmpini};
+        out << "#this is a comment line\n";
+        out << "[default]\n";
+        out << "two=[2,3]\n";
+        out << "three=[1,2,3]\n";
+    }
+
+    std::vector<int> two, three;
+    app.add_option("--two", two)->expected(2)->required();
+    app.add_option("--three", three)->required();
+
+    run();
+
+    EXPECT_EQ(std::vector<int>({2, 3}), two);
+    EXPECT_EQ(std::vector<int>({1, 2, 3}), three);
+}
+
+TEST_F(TApp, IniVectorCsep) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "#this is a comment line\n";
+        out << "[default]\n";
+        out << "two=[2,3]\n";
+        out << "three=1,2,3\n";
+    }
+
+    std::vector<int> two, three;
+    app.add_option("--two", two)->expected(2)->required();
+    app.add_option("--three", three)->required();
+
+    run();
+
+    EXPECT_EQ(std::vector<int>({2, 3}), two);
+    EXPECT_EQ(std::vector<int>({1, 2, 3}), three);
+}
+
+TEST_F(TApp, IniVectorMultiple) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "#this is a comment line\n";
+        out << "[default]\n";
+        out << "two=2\n";
+        out << "two=3\n";
+        out << "three=1\n";
+        out << "three=2\n";
+        out << "three=3\n";
+    }
+
+    std::vector<int> two, three;
+    app.add_option("--two", two)->expected(2)->required();
+    app.add_option("--three", three)->required();
+
+    run();
+
+    EXPECT_EQ(std::vector<int>({2, 3}), two);
+    EXPECT_EQ(std::vector<int>({1, 2, 3}), three);
+}
+
+TEST_F(TApp, IniLayered) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+        out << "[subcom]" << std::endl;
+        out << "val=2" << std::endl;
+        out << "subsubcom.val=3" << std::endl;
+    }
+
+    int one{0}, two{0}, three{0};
+    app.add_option("--val", one);
+    auto subcom = app.add_subcommand("subcom");
+    subcom->add_option("--val", two);
+    auto subsubcom = subcom->add_subcommand("subsubcom");
+    subsubcom->add_option("--val", three);
+
+    run();
+
+    EXPECT_EQ(1, one);
+    EXPECT_EQ(2, two);
+    EXPECT_EQ(3, three);
+
+    EXPECT_EQ(subcom->count(), 0U);
+    EXPECT_FALSE(*subcom);
+}
+
+TEST_F(TApp, IniLayeredDotSection) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+        out << "[subcom]" << std::endl;
+        out << "val=2" << std::endl;
+        out << "[subcom.subsubcom]" << std::endl;
+        out << "val=3" << std::endl;
+    }
+
+    int one{0}, two{0}, three{0};
+    app.add_option("--val", one);
+    auto subcom = app.add_subcommand("subcom");
+    subcom->add_option("--val", two);
+    auto subsubcom = subcom->add_subcommand("subsubcom");
+    subsubcom->add_option("--val", three);
+
+    run();
+
+    EXPECT_EQ(1, one);
+    EXPECT_EQ(2, two);
+    EXPECT_EQ(3, three);
+
+    EXPECT_EQ(subcom->count(), 0U);
+    EXPECT_FALSE(*subcom);
+}
+
+TEST_F(TApp, IniSubcommandConfigurable) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+        out << "[subcom]" << std::endl;
+        out << "val=2" << std::endl;
+        out << "subsubcom.val=3" << std::endl;
+    }
+
+    int one{0}, two{0}, three{0};
+    app.add_option("--val", one);
+    auto subcom = app.add_subcommand("subcom");
+    subcom->configurable();
+    subcom->add_option("--val", two);
+    auto subsubcom = subcom->add_subcommand("subsubcom");
+    subsubcom->add_option("--val", three);
+
+    run();
+
+    EXPECT_EQ(1, one);
+    EXPECT_EQ(2, two);
+    EXPECT_EQ(3, three);
+
+    EXPECT_EQ(subcom->count(), 1U);
+    EXPECT_TRUE(*subcom);
+    EXPECT_TRUE(app.got_subcommand(subcom));
+}
+
+TEST_F(TApp, IniSubcommandConfigurablePreParse) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+        out << "[subcom]" << std::endl;
+        out << "val=2" << std::endl;
+        out << "subsubcom.val=3" << std::endl;
+    }
+
+    int one{0}, two{0}, three{0}, four{0};
+    app.add_option("--val", one);
+    auto subcom = app.add_subcommand("subcom");
+    auto subcom2 = app.add_subcommand("subcom2");
+    subcom->configurable();
+    std::vector<std::size_t> parse_c;
+    subcom->preparse_callback([&parse_c](std::size_t cnt) { parse_c.push_back(cnt); });
+    subcom->add_option("--val", two);
+    subcom2->add_option("--val", four);
+    subcom2->preparse_callback([&parse_c](std::size_t cnt) { parse_c.push_back(cnt + 2623); });
+    auto subsubcom = subcom->add_subcommand("subsubcom");
+    subsubcom->add_option("--val", three);
+
+    run();
+
+    EXPECT_EQ(1, one);
+    EXPECT_EQ(2, two);
+    EXPECT_EQ(3, three);
+    EXPECT_EQ(0, four);
+
+    EXPECT_EQ(parse_c.size(), 1U);
+    EXPECT_EQ(parse_c[0], 2U);
+
+    EXPECT_EQ(subcom2->count(), 0U);
+}
+
+TEST_F(TApp, IniSubcommandConfigurableParseComplete) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+        out << "[subcom]" << std::endl;
+        out << "val=2" << std::endl;
+        out << "[subcom.subsubcom]" << std::endl;
+        out << "val=3" << std::endl;
+    }
+
+    int one{0}, two{0}, three{0}, four{0};
+    app.add_option("--val", one);
+    auto subcom = app.add_subcommand("subcom");
+    auto subcom2 = app.add_subcommand("subcom2");
+    subcom->configurable();
+    std::vector<std::size_t> parse_c;
+    subcom->parse_complete_callback([&parse_c]() { parse_c.push_back(58); });
+    subcom->add_option("--val", two);
+    subcom2->add_option("--val", four);
+    subcom2->parse_complete_callback([&parse_c]() { parse_c.push_back(2623); });
+    auto subsubcom = subcom->add_subcommand("subsubcom");
+    // configurable should be inherited
+    subsubcom->parse_complete_callback([&parse_c]() { parse_c.push_back(68); });
+    subsubcom->add_option("--val", three);
+
+    run();
+
+    EXPECT_EQ(1, one);
+    EXPECT_EQ(2, two);
+    EXPECT_EQ(3, three);
+    EXPECT_EQ(0, four);
+
+    ASSERT_EQ(parse_c.size(), 2u);
+    EXPECT_EQ(parse_c[0], 68U);
+    EXPECT_EQ(parse_c[1], 58U);
+    EXPECT_EQ(subsubcom->count(), 1u);
+    EXPECT_EQ(subcom2->count(), 0u);
+}
+
+TEST_F(TApp, IniSubcommandMultipleSections) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+        out << "[subcom]" << std::endl;
+        out << "val=2" << std::endl;
+        out << "[subcom.subsubcom]" << std::endl;
+        out << "val=3" << std::endl;
+        out << "[subcom2]" << std::endl;
+        out << "val=4" << std::endl;
+    }
+
+    int one{0}, two{0}, three{0}, four{0};
+    app.add_option("--val", one);
+    auto subcom = app.add_subcommand("subcom");
+    auto subcom2 = app.add_subcommand("subcom2");
+    subcom->configurable();
+    std::vector<std::size_t> parse_c;
+    subcom->parse_complete_callback([&parse_c]() { parse_c.push_back(58); });
+    subcom->add_option("--val", two);
+    subcom2->add_option("--val", four);
+    subcom2->parse_complete_callback([&parse_c]() { parse_c.push_back(2623); });
+    subcom2->configurable(false);
+    auto subsubcom = subcom->add_subcommand("subsubcom");
+    // configurable should be inherited
+    subsubcom->parse_complete_callback([&parse_c]() { parse_c.push_back(68); });
+    subsubcom->add_option("--val", three);
+
+    run();
+
+    EXPECT_EQ(1, one);
+    EXPECT_EQ(2, two);
+    EXPECT_EQ(3, three);
+    EXPECT_EQ(4, four);
+
+    ASSERT_EQ(parse_c.size(), 2u);
+    EXPECT_EQ(parse_c[0], 68U);
+    EXPECT_EQ(parse_c[1], 58U);
+    EXPECT_EQ(subsubcom->count(), 1u);
+    EXPECT_EQ(subcom2->count(), 0u); // not configurable but value is updated
+}
+
+TEST_F(TApp, DuplicateSubcommandCallbacks) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[[foo]]" << std::endl;
+        out << "[[foo]]" << std::endl;
+        out << "[[foo]]" << std::endl;
+    }
+
+    auto foo = app.add_subcommand("foo");
+    int count{0};
+    foo->callback([&count]() { ++count; });
+    foo->immediate_callback();
+    EXPECT_TRUE(foo->get_immediate_callback());
+    foo->configurable();
+
+    run();
+    EXPECT_EQ(count, 3);
+}
+
+TEST_F(TApp, IniFailure) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+    app.allow_config_extras(false);
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+    }
+
+    EXPECT_THROW(run(), CLI::ConfigError);
+}
+
+TEST_F(TApp, IniConfigurable) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+    bool value{false};
+    app.add_flag("--val", value)->configurable(true);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+    }
+
+    ASSERT_NO_THROW(run());
+    EXPECT_TRUE(value);
+}
+
+TEST_F(TApp, IniNotConfigurable) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+    bool value{false};
+    app.add_flag("--val", value)->configurable(false);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+    }
+
+    EXPECT_THROW(run(), CLI::ConfigError);
+}
+
+TEST_F(TApp, IniSubFailure) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.add_subcommand("other");
+    app.set_config("--config", tmpini);
+    app.allow_config_extras(false);
+    {
+        std::ofstream out{tmpini};
+        out << "[other]" << std::endl;
+        out << "val=1" << std::endl;
+    }
+
+    EXPECT_THROW(run(), CLI::ConfigError);
+}
+
+TEST_F(TApp, IniNoSubFailure) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+    app.allow_config_extras(CLI::config_extras_mode::error);
+    {
+        std::ofstream out{tmpini};
+        out << "[other]" << std::endl;
+        out << "val=1" << std::endl;
+    }
+
+    EXPECT_THROW(run(), CLI::ConfigError);
+}
+
+TEST_F(TApp, IniFlagConvertFailure) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.add_flag("--flag");
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "flag=moobook" << std::endl;
+    }
+    run();
+    bool result{false};
+    auto *opt = app.get_option("--flag");
+    EXPECT_THROW(opt->results(result), CLI::ConversionError);
+    std::string res;
+    opt->results(res);
+    EXPECT_EQ(res, "moobook");
+}
+
+TEST_F(TApp, IniFlagNumbers) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    bool boo{false};
+    app.add_flag("--flag", boo);
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "flag=3" << std::endl;
+    }
+
+    ASSERT_NO_THROW(run());
+    EXPECT_TRUE(boo);
+}
+
+TEST_F(TApp, IniFlagDual) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    bool boo{false};
+    app.add_flag("--flag", boo);
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "flag=1 1" << std::endl;
+    }
+
+    EXPECT_THROW(run(), CLI::ConversionError);
+}
+
+TEST_F(TApp, IniFlagText) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    bool flag1{false}, flag2{false}, flag3{false}, flag4{false};
+    app.add_flag("--flag1", flag1);
+    app.add_flag("--flag2", flag2);
+    app.add_flag("--flag3", flag3);
+    app.add_flag("--flag4", flag4);
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "flag1=true" << std::endl;
+        out << "flag2=on" << std::endl;
+        out << "flag3=off" << std::endl;
+        out << "flag4=1" << std::endl;
+    }
+
+    run();
+
+    EXPECT_TRUE(flag1);
+    EXPECT_TRUE(flag2);
+    EXPECT_FALSE(flag3);
+    EXPECT_TRUE(flag4);
+}
+
+TEST_F(TApp, IniFlags) {
+    TempFile tmpini{"TestIniTmp.ini"};
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=2" << std::endl;
+        out << "three=true" << std::endl;
+        out << "four=on" << std::endl;
+        out << "five" << std::endl;
+    }
+
+    int two{0};
+    bool three{false}, four{false}, five{false};
+    app.add_flag("--two", two);
+    app.add_flag("--three", three);
+    app.add_flag("--four", four);
+    app.add_flag("--five", five);
+
+    run();
+
+    EXPECT_EQ(2, two);
+    EXPECT_TRUE(three);
+    EXPECT_TRUE(four);
+    EXPECT_TRUE(five);
+}
+
+TEST_F(TApp, IniFalseFlags) {
+    TempFile tmpini{"TestIniTmp.ini"};
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=-2" << std::endl;
+        out << "three=false" << std::endl;
+        out << "four=1" << std::endl;
+        out << "five" << std::endl;
+    }
+
+    int two{0};
+    bool three{false}, four{false}, five{false};
+    app.add_flag("--two", two);
+    app.add_flag("--three", three);
+    app.add_flag("--four", four);
+    app.add_flag("--five", five);
+
+    run();
+
+    EXPECT_EQ(-2, two);
+    EXPECT_FALSE(three);
+    EXPECT_TRUE(four);
+    EXPECT_TRUE(five);
+}
+
+TEST_F(TApp, IniFalseFlagsDef) {
+    TempFile tmpini{"TestIniTmp.ini"};
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=2" << std::endl;
+        out << "three=true" << std::endl;
+        out << "four=on" << std::endl;
+        out << "five" << std::endl;
+    }
+
+    int two{0};
+    bool three{false}, four{false}, five{false};
+    app.add_flag("--two{false}", two);
+    app.add_flag("--three", three);
+    app.add_flag("!--four", four);
+    app.add_flag("--five", five);
+
+    run();
+
+    EXPECT_EQ(-2, two);
+    EXPECT_TRUE(three);
+    EXPECT_FALSE(four);
+    EXPECT_TRUE(five);
+}
+
+TEST_F(TApp, IniFalseFlagsDefDisableOverrideError) {
+    TempFile tmpini{"TestIniTmp.ini"};
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=2" << std::endl;
+        out << "four=on" << std::endl;
+        out << "five" << std::endl;
+    }
+
+    int two{0};
+    bool four{false}, five{false};
+    app.add_flag("--two{false}", two)->disable_flag_override();
+    app.add_flag("!--four", four);
+    app.add_flag("--five", five);
+
+    EXPECT_THROW(run(), CLI::ArgumentMismatch);
+}
+
+TEST_F(TApp, IniFalseFlagsDefDisableOverrideSuccess) {
+    TempFile tmpini{"TestIniTmp.ini"};
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=2" << std::endl;
+        out << "four={}" << std::endl;
+        out << "val=15" << std::endl;
+    }
+
+    int two{0}, four{0}, val{0};
+    app.add_flag("--two{2}", two)->disable_flag_override();
+    app.add_flag("--four{4}", four)->disable_flag_override();
+    app.add_flag("--val", val);
+
+    run();
+
+    EXPECT_EQ(2, two);
+    EXPECT_EQ(4, four);
+    EXPECT_EQ(15, val);
+}
+
+TEST_F(TApp, IniOutputSimple) {
+
+    int v{0};
+    app.add_option("--simple", v);
+
+    args = {"--simple=3"};
+
+    run();
+
+    std::string str = app.config_to_str();
+    EXPECT_EQ("simple=3\n", str);
+}
+
+TEST_F(TApp, IniOutputNoConfigurable) {
+
+    int v1{0}, v2{0};
+    app.add_option("--simple", v1);
+    app.add_option("--noconf", v2)->configurable(false);
+
+    args = {"--simple=3", "--noconf=2"};
+
+    run();
+
+    std::string str = app.config_to_str();
+    EXPECT_EQ("simple=3\n", str);
+}
+
+TEST_F(TApp, IniOutputShortSingleDescription) {
+    std::string flag = "some_flag";
+    const std::string description = "Some short description.";
+    app.add_flag("--" + flag, description);
+
+    run();
+
+    std::string str = app.config_to_str(true, true);
+    EXPECT_THAT(str, HasSubstr("; " + description + "\n" + flag + "=false\n"));
+}
+
+TEST_F(TApp, IniOutputShortDoubleDescription) {
+    std::string flag1 = "flagnr1";
+    std::string flag2 = "flagnr2";
+    const std::string description1 = "First description.";
+    const std::string description2 = "Second description.";
+    app.add_flag("--" + flag1, description1);
+    app.add_flag("--" + flag2, description2);
+
+    run();
+
+    std::string str = app.config_to_str(true, true);
+    EXPECT_THAT(
+        str, HasSubstr("; " + description1 + "\n" + flag1 + "=false\n\n; " + description2 + "\n" + flag2 + "=false\n"));
+}
+
+TEST_F(TApp, IniOutputGroups) {
+    std::string flag1 = "flagnr1";
+    std::string flag2 = "flagnr2";
+    const std::string description1 = "First description.";
+    const std::string description2 = "Second description.";
+    app.add_flag("--" + flag1, description1)->group("group1");
+    app.add_flag("--" + flag2, description2)->group("group2");
+
+    run();
+
+    std::string str = app.config_to_str(true, true);
+    EXPECT_THAT(str, HasSubstr("group1"));
+    EXPECT_THAT(str, HasSubstr("group2"));
+}
+
+TEST_F(TApp, IniOutputHiddenOptions) {
+    std::string flag1 = "flagnr1";
+    std::string flag2 = "flagnr2";
+    double val{12.7};
+    const std::string description1 = "First description.";
+    const std::string description2 = "Second description.";
+    app.add_flag("--" + flag1, description1)->group("group1");
+    app.add_flag("--" + flag2, description2)->group("group2");
+    app.add_option("--dval", val, "", true)->group("");
+
+    run();
+
+    std::string str = app.config_to_str(true, true);
+    EXPECT_THAT(str, HasSubstr("group1"));
+    EXPECT_THAT(str, HasSubstr("group2"));
+    EXPECT_THAT(str, HasSubstr("dval=12.7"));
+    auto loc = str.find("dval=12.7");
+    auto locg1 = str.find("group1");
+    EXPECT_GT(locg1, loc);
+    // make sure it doesn't come twice
+    loc = str.find("dval=12.7", loc + 4);
+    EXPECT_EQ(loc, std::string::npos);
+}
+
+TEST_F(TApp, IniOutputMultiLineDescription) {
+    std::string flag = "some_flag";
+    const std::string description = "Some short description.\nThat has lines.";
+    app.add_flag("--" + flag, description);
+
+    run();
+
+    std::string str = app.config_to_str(true, true);
+    EXPECT_THAT(str, HasSubstr("; Some short description.\n"));
+    EXPECT_THAT(str, HasSubstr("; That has lines.\n"));
+    EXPECT_THAT(str, HasSubstr(flag + "=false\n"));
+}
+
+TEST_F(TApp, IniOutputOptionGroup) {
+    std::string flag1 = "flagnr1";
+    std::string flag2 = "flagnr2";
+    double val{12.7};
+    const std::string description1 = "First description.";
+    const std::string description2 = "Second description.";
+    app.add_flag("--" + flag1, description1)->group("group1");
+    app.add_flag("--" + flag2, description2)->group("group2");
+    auto og = app.add_option_group("group3", "g3 desc");
+    og->add_option("--dval", val, "", true)->group("");
+
+    run();
+
+    std::string str = app.config_to_str(true, true);
+    EXPECT_THAT(str, HasSubstr("group1"));
+    EXPECT_THAT(str, HasSubstr("group2"));
+    EXPECT_THAT(str, HasSubstr("dval=12.7"));
+    EXPECT_THAT(str, HasSubstr("group3"));
+    EXPECT_THAT(str, HasSubstr("g3 desc"));
+    auto loc = str.find("dval=12.7");
+    auto locg1 = str.find("group1");
+    auto locg3 = str.find("group3");
+    EXPECT_LT(locg1, loc);
+    // make sure it doesn't come twice
+    loc = str.find("dval=12.7", loc + 4);
+    EXPECT_EQ(loc, std::string::npos);
+    EXPECT_GT(locg3, locg1);
+}
+
+TEST_F(TApp, IniOutputVector) {
+
+    std::vector<int> v;
+    app.add_option("--vector", v);
+
+    args = {"--vector", "1", "2", "3"};
+
+    run();
+
+    std::string str = app.config_to_str();
+    EXPECT_EQ("vector=1 2 3\n", str);
+}
+
+TEST_F(TApp, IniOutputVectorTOML) {
+
+    std::vector<int> v;
+    app.add_option("--vector", v);
+    app.config_formatter(std::make_shared<CLI::ConfigTOML>());
+    args = {"--vector", "1", "2", "3"};
+
+    run();
+
+    std::string str = app.config_to_str();
+    EXPECT_EQ("vector=[1, 2, 3]\n", str);
+}
+
+TEST_F(TApp, IniOutputVectorCustom) {
+
+    std::vector<int> v;
+    app.add_option("--vector", v);
+    auto V = std::make_shared<CLI::ConfigBase>();
+    V->arrayBounds('{', '}')->arrayDelimiter(';')->valueSeparator(':');
+    app.config_formatter(V);
+    args = {"--vector", "1", "2", "3"};
+
+    run();
+
+    std::string str = app.config_to_str();
+    EXPECT_EQ("vector:{1; 2; 3}\n", str);
+}
+
+TEST_F(TApp, IniOutputFlag) {
+
+    int v{0}, q{0};
+    app.add_option("--simple", v);
+    app.add_flag("--nothing");
+    app.add_flag("--onething");
+    app.add_flag("--something", q);
+
+    args = {"--simple=3", "--onething", "--something", "--something"};
+
+    run();
+
+    std::string str = app.config_to_str();
+    EXPECT_THAT(str, HasSubstr("simple=3"));
+    EXPECT_THAT(str, Not(HasSubstr("nothing")));
+    EXPECT_THAT(str, HasSubstr("onething=true"));
+    EXPECT_THAT(str, HasSubstr("something=true true"));
+
+    str = app.config_to_str(true);
+    EXPECT_THAT(str, HasSubstr("nothing"));
+}
+
+TEST_F(TApp, IniOutputSet) {
+
+    int v{0};
+    app.add_option("--simple", v)->check(CLI::IsMember({1, 2, 3}));
+
+    args = {"--simple=2"};
+
+    run();
+
+    std::string str = app.config_to_str();
+    EXPECT_THAT(str, HasSubstr("simple=2"));
+}
+
+TEST_F(TApp, IniOutputDefault) {
+
+    int v{7};
+    app.add_option("--simple", v, "", true);
+
+    run();
+
+    std::string str = app.config_to_str();
+    EXPECT_THAT(str, Not(HasSubstr("simple=7")));
+
+    str = app.config_to_str(true);
+    EXPECT_THAT(str, HasSubstr("simple=7"));
+}
+
+TEST_F(TApp, IniOutputSubcom) {
+
+    app.add_flag("--simple");
+    auto subcom = app.add_subcommand("other");
+    subcom->add_flag("--newer");
+
+    args = {"--simple", "other", "--newer"};
+    run();
+
+    std::string str = app.config_to_str();
+    EXPECT_THAT(str, HasSubstr("simple=true"));
+    EXPECT_THAT(str, HasSubstr("other.newer=true"));
+}
+
+TEST_F(TApp, IniOutputSubcomConfigurable) {
+
+    app.add_flag("--simple");
+    auto subcom = app.add_subcommand("other")->configurable();
+    subcom->add_flag("--newer");
+
+    args = {"--simple", "other", "--newer"};
+    run();
+
+    std::string str = app.config_to_str();
+    EXPECT_THAT(str, HasSubstr("simple=true"));
+    EXPECT_THAT(str, HasSubstr("[other]"));
+    EXPECT_THAT(str, HasSubstr("newer=true"));
+    EXPECT_EQ(str.find("other.newer=true"), std::string::npos);
+}
+
+TEST_F(TApp, IniOutputSubsubcom) {
+
+    app.add_flag("--simple");
+    auto subcom = app.add_subcommand("other");
+    subcom->add_flag("--newer");
+    auto subsubcom = subcom->add_subcommand("sub2");
+    subsubcom->add_flag("--newest");
+
+    args = {"--simple", "other", "--newer", "sub2", "--newest"};
+    run();
+
+    std::string str = app.config_to_str();
+    EXPECT_THAT(str, HasSubstr("simple=true"));
+    EXPECT_THAT(str, HasSubstr("other.newer=true"));
+    EXPECT_THAT(str, HasSubstr("other.sub2.newest=true"));
+}
+
+TEST_F(TApp, IniOutputSubsubcomConfigurable) {
+
+    app.add_flag("--simple");
+    auto subcom = app.add_subcommand("other")->configurable();
+    subcom->add_flag("--newer");
+
+    auto subsubcom = subcom->add_subcommand("sub2");
+    subsubcom->add_flag("--newest");
+
+    args = {"--simple", "other", "--newer", "sub2", "--newest"};
+    run();
+
+    std::string str = app.config_to_str();
+    EXPECT_THAT(str, HasSubstr("simple=true"));
+    EXPECT_THAT(str, HasSubstr("[other]"));
+    EXPECT_THAT(str, HasSubstr("newer=true"));
+    EXPECT_THAT(str, HasSubstr("[other.sub2]"));
+    EXPECT_THAT(str, HasSubstr("newest=true"));
+    EXPECT_EQ(str.find("sub2.newest=true"), std::string::npos);
+}
+
+TEST_F(TApp, IniOutputSubsubcomConfigurableDeep) {
+
+    app.add_flag("--simple");
+    auto subcom = app.add_subcommand("other")->configurable();
+    subcom->add_flag("--newer");
+
+    auto subsubcom = subcom->add_subcommand("sub2");
+    subsubcom->add_flag("--newest");
+    auto sssscom = subsubcom->add_subcommand("sub-level2");
+    subsubcom->add_flag("--still_newer");
+    auto s5com = sssscom->add_subcommand("sub-level3");
+    s5com->add_flag("--absolute_newest");
+
+    args = {"--simple", "other", "sub2", "sub-level2", "sub-level3", "--absolute_newest"};
+    run();
+
+    std::string str = app.config_to_str();
+    EXPECT_THAT(str, HasSubstr("simple=true"));
+    EXPECT_THAT(str, HasSubstr("[other.sub2.sub-level2.sub-level3]"));
+    EXPECT_THAT(str, HasSubstr("absolute_newest=true"));
+    EXPECT_EQ(str.find(".absolute_newest=true"), std::string::npos);
+}
+
+TEST_F(TApp, IniQuotedOutput) {
+
+    std::string val1;
+    app.add_option("--val1", val1);
+
+    std::string val2;
+    app.add_option("--val2", val2);
+
+    args = {"--val1", "I am a string", "--val2", R"(I am a "confusing" string)"};
+
+    run();
+
+    EXPECT_EQ("I am a string", val1);
+    EXPECT_EQ("I am a \"confusing\" string", val2);
+
+    std::string str = app.config_to_str();
+    EXPECT_THAT(str, HasSubstr("val1=\"I am a string\""));
+    EXPECT_THAT(str, HasSubstr("val2='I am a \"confusing\" string'"));
+}
+
+TEST_F(TApp, DefaultsIniQuotedOutput) {
+
+    std::string val1{"I am a string"};
+    app.add_option("--val1", val1, "", true);
+
+    std::string val2{R"(I am a "confusing" string)"};
+    app.add_option("--val2", val2, "", true);
+
+    run();
+
+    std::string str = app.config_to_str(true);
+    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);
+}
+
+TEST_F(TApp, ConfigWriteReadWrite) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.add_flag("--flag");
+    run();
+
+    // Save config, with default values too
+    std::string config1 = app.config_to_str(true, true);
+    {
+        std::ofstream out{tmpini};
+        out << config1 << std::endl;
+    }
+
+    app.set_config("--config", tmpini, "Read an ini file", true);
+    run();
+
+    std::string config2 = app.config_to_str(true, true);
+
+    EXPECT_EQ(config1, config2);
+}
diff --git a/packages/CLI11/tests/CreationTest.cpp b/packages/CLI11/tests/CreationTest.cpp
index 5e428860276b81ad8af8cfdbb890cfab44676295..7143e3efbc65236fa62b8f881764c19a40b1b9ca 100644
--- a/packages/CLI11/tests/CreationTest.cpp
+++ b/packages/CLI11/tests/CreationTest.cpp
@@ -164,55 +164,15 @@ TEST_F(TApp, MultipleSubcomNoMatchingInplaceUnderscore2) {
 TEST_F(TApp, IncorrectConstructionFlagPositional1) { EXPECT_THROW(app.add_flag("cat"), CLI::IncorrectConstruction); }
 
 TEST_F(TApp, IncorrectConstructionFlagPositional2) {
-    int x;
+    int x{0};
     EXPECT_THROW(app.add_flag("cat", x), CLI::IncorrectConstruction);
 }
 
 TEST_F(TApp, IncorrectConstructionFlagPositional3) {
-    bool x;
+    bool x{false};
     EXPECT_THROW(app.add_flag("cat", x), CLI::IncorrectConstruction);
 }
 
-TEST_F(TApp, IncorrectConstructionFlagExpected) {
-    auto cat = app.add_flag("--cat");
-    EXPECT_THROW(cat->expected(0), CLI::IncorrectConstruction);
-    EXPECT_THROW(cat->expected(1), CLI::IncorrectConstruction);
-}
-
-TEST_F(TApp, IncorrectConstructionOptionAsFlag) {
-    int x;
-    auto cat = app.add_option("--cat", x);
-    EXPECT_NO_THROW(cat->expected(1));
-    EXPECT_THROW(cat->expected(0), CLI::IncorrectConstruction);
-    EXPECT_THROW(cat->expected(2), CLI::IncorrectConstruction);
-}
-
-TEST_F(TApp, IncorrectConstructionOptionAsVector) {
-    int x;
-    auto cat = app.add_option("--cat", x);
-    EXPECT_THROW(cat->expected(2), CLI::IncorrectConstruction);
-}
-
-TEST_F(TApp, IncorrectConstructionVectorAsFlag) {
-    std::vector<int> x;
-    auto cat = app.add_option("--cat", x);
-    EXPECT_THROW(cat->expected(0), CLI::IncorrectConstruction);
-}
-
-TEST_F(TApp, IncorrectConstructionVectorTakeLast) {
-    std::vector<int> vec;
-    auto cat = app.add_option("--vec", vec);
-    EXPECT_THROW(cat->multi_option_policy(CLI::MultiOptionPolicy::TakeLast), CLI::IncorrectConstruction);
-}
-
-TEST_F(TApp, IncorrectConstructionTakeLastExpected) {
-    std::vector<int> vec;
-    auto cat = app.add_option("--vec", vec);
-    cat->expected(1);
-    ASSERT_NO_THROW(cat->multi_option_policy(CLI::MultiOptionPolicy::TakeLast));
-    EXPECT_THROW(cat->expected(2), CLI::IncorrectConstruction);
-}
-
 TEST_F(TApp, IncorrectConstructionNeedsCannotFind) {
     auto cat = app.add_flag("--cat");
     EXPECT_THROW(cat->needs("--nothing"), CLI::IncorrectConstruction);
@@ -260,7 +220,7 @@ TEST_F(TApp, CheckName) {
     auto long2 = app.add_flag("--Long2");
     auto short1 = app.add_flag("-a");
     auto short2 = app.add_flag("-B");
-    int x, y;
+    int x{0}, y{0};
     auto pos1 = app.add_option("pos1", x);
     auto pos2 = app.add_option("pOs2", y);
 
@@ -288,7 +248,7 @@ TEST_F(TApp, CheckNameNoCase) {
     auto long2 = app.add_flag("--Long2")->ignore_case();
     auto short1 = app.add_flag("-a")->ignore_case();
     auto short2 = app.add_flag("-B")->ignore_case();
-    int x, y;
+    int x{0}, y{0};
     auto pos1 = app.add_option("pos1", x)->ignore_case();
     auto pos2 = app.add_option("pOs2", y)->ignore_case();
 
@@ -315,7 +275,7 @@ TEST_F(TApp, CheckNameNoUnderscore) {
     auto long1 = app.add_flag("--longoption1")->ignore_underscore();
     auto long2 = app.add_flag("--long_option2")->ignore_underscore();
 
-    int x, y;
+    int x{0}, y{0};
     auto pos1 = app.add_option("pos_option_1", x)->ignore_underscore();
     auto pos2 = app.add_option("posoption2", y)->ignore_underscore();
 
@@ -346,7 +306,7 @@ TEST_F(TApp, CheckNameNoCaseNoUnderscore) {
     auto long1 = app.add_flag("--LongoptioN1")->ignore_underscore()->ignore_case();
     auto long2 = app.add_flag("--long_Option2")->ignore_case()->ignore_underscore();
 
-    int x, y;
+    int x{0}, y{0};
     auto pos1 = app.add_option("pos_Option_1", x)->ignore_underscore()->ignore_case();
     auto pos2 = app.add_option("posOption2", y)->ignore_case()->ignore_underscore();
 
@@ -374,7 +334,7 @@ TEST_F(TApp, CheckNameNoCaseNoUnderscore) {
 }
 
 TEST_F(TApp, PreSpaces) {
-    int x;
+    int x{0};
     auto myapp = app.add_option(" -a, --long, other", x);
 
     EXPECT_TRUE(myapp->check_lname("long"));
@@ -383,7 +343,7 @@ TEST_F(TApp, PreSpaces) {
 }
 
 TEST_F(TApp, AllSpaces) {
-    int x;
+    int x{0};
     auto myapp = app.add_option(" -a , --long , other ", x);
 
     EXPECT_TRUE(myapp->check_lname("long"));
@@ -395,7 +355,7 @@ TEST_F(TApp, OptionFromDefaults) {
     app.option_defaults()->required();
 
     // Options should remember defaults
-    int x;
+    int x{0};
     auto opt = app.add_option("--simple", x);
     EXPECT_TRUE(opt->get_required());
 
@@ -451,7 +411,7 @@ TEST_F(TApp, OptionFromDefaultsSubcommands) {
 }
 
 TEST_F(TApp, GetNameCheck) {
-    int x;
+    int x{0};
     auto a = app.add_flag("--that");
     auto b = app.add_flag("-x");
     auto c = app.add_option("pos", x);
@@ -483,6 +443,7 @@ TEST_F(TApp, SubcommandDefaults) {
     EXPECT_FALSE(app.get_allow_windows_style_options());
 #endif
     EXPECT_FALSE(app.get_fallthrough());
+    EXPECT_FALSE(app.get_configurable());
     EXPECT_FALSE(app.get_validate_positionals());
 
     EXPECT_EQ(app.get_footer(), "");
@@ -495,6 +456,7 @@ TEST_F(TApp, SubcommandDefaults) {
     app.immediate_callback();
     app.ignore_case();
     app.ignore_underscore();
+    app.configurable();
 #ifdef _WIN32
     app.allow_windows_style_options(false);
 #else
@@ -522,6 +484,7 @@ TEST_F(TApp, SubcommandDefaults) {
 #endif
     EXPECT_TRUE(app2->get_fallthrough());
     EXPECT_TRUE(app2->get_validate_positionals());
+    EXPECT_TRUE(app2->get_configurable());
     EXPECT_EQ(app2->get_footer(), "footy");
     EXPECT_EQ(app2->get_group(), "Stuff");
     EXPECT_EQ(app2->get_require_subcommand_min(), 0u);
@@ -560,19 +523,19 @@ TEST_F(TApp, SubcommandMinMax) {
 }
 
 TEST_F(TApp, GetOptionList) {
-    int two;
+    int two{0};
     auto flag = app.add_flag("--one");
     auto opt = app.add_option("--two", two);
 
     const CLI::App &const_app = app; // const alias to force use of const-methods
     std::vector<const CLI::Option *> opt_list = const_app.get_options();
 
-    ASSERT_EQ(opt_list.size(), static_cast<size_t>(3));
+    ASSERT_EQ(opt_list.size(), static_cast<std::size_t>(3));
     EXPECT_EQ(opt_list.at(1), flag);
     EXPECT_EQ(opt_list.at(2), opt);
 
     std::vector<CLI::Option *> nonconst_opt_list = app.get_options();
-    for(size_t i = 0; i < opt_list.size(); ++i) {
+    for(std::size_t i = 0; i < opt_list.size(); ++i) {
         EXPECT_EQ(nonconst_opt_list.at(i), opt_list.at(i));
     }
 }
@@ -741,10 +704,10 @@ TEST(ValidatorTests, ValidatorDefaults) {
 
 class Unstreamable {
   private:
-    int x_ = -1;
+    int x_{-1};
 
   public:
-    Unstreamable() {}
+    Unstreamable() = default;
     int get_x() const { return x_; }
     void set_x(int x) { x_ = x; }
 };
@@ -759,7 +722,8 @@ std::istream &operator>>(std::istream &in, Unstreamable &value) {
     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");
+static_assert(CLI::detail::is_istreamable<Unstreamable>::value,
+              "Unstreamable type is still unstreamable and it should be");
 
 TEST_F(TApp, MakeUnstreamableOptions) {
     Unstreamable value;
diff --git a/packages/CLI11/tests/DeprecatedTest.cpp b/packages/CLI11/tests/DeprecatedTest.cpp
index 9dc064ec31ab904eba6aaee05e19e4f430f6a5b5..367e6fd883805dda4bfea88615c8863233080661 100644
--- a/packages/CLI11/tests/DeprecatedTest.cpp
+++ b/packages/CLI11/tests/DeprecatedTest.cpp
@@ -30,15 +30,6 @@ TEST_F(TApp, SetWithDefaultsConversion) {
     EXPECT_THROW(run(), CLI::ValidationError);
 }
 
-TEST_F(TApp, SetWithDefaultsIC) {
-    std::string someint = "ho";
-    app.add_set_ignore_case("-a", someint, {"Hi", "Ho"}, "", true);
-
-    args = {"-aHi", "-aHo"};
-
-    EXPECT_THROW(run(), CLI::ArgumentMismatch);
-}
-
 TEST_F(TApp, InSet) {
 
     std::string choice;
@@ -70,23 +61,6 @@ TEST_F(TApp, InSetWithDefault) {
     EXPECT_THROW(run(), CLI::ValidationError);
 }
 
-TEST_F(TApp, InCaselessSetWithDefault) {
-
-    std::string choice = "one";
-    app.add_set_ignore_case("-q,--quick", choice, {"one", "two", "three"}, "", true);
-
-    run();
-    EXPECT_EQ("one", choice);
-
-    args = {"--quick", "tWo"};
-
-    run();
-    EXPECT_EQ("two", choice);
-
-    args = {"--quick", "four"};
-    EXPECT_THROW(run(), CLI::ValidationError);
-}
-
 TEST_F(TApp, InIntSet) {
 
     int choice;
@@ -144,131 +118,6 @@ TEST_F(TApp, FailMutableSet) {
     EXPECT_THROW(run(), CLI::ValidationError);
 }
 
-TEST_F(TApp, InSetIgnoreCase) {
-
-    std::string choice;
-    app.add_set_ignore_case("-q,--quick", choice, {"one", "Two", "THREE"});
-
-    args = {"--quick", "One"};
-    run();
-    EXPECT_EQ("one", choice);
-
-    args = {"--quick", "two"};
-    run();
-    EXPECT_EQ("Two", choice); // Keeps caps from set
-
-    args = {"--quick", "ThrEE"};
-    run();
-    EXPECT_EQ("THREE", choice); // Keeps caps from set
-
-    args = {"--quick", "four"};
-    EXPECT_THROW(run(), CLI::ValidationError);
-
-    args = {"--quick=one", "--quick=two"};
-    EXPECT_THROW(run(), CLI::ArgumentMismatch);
-}
-
-TEST_F(TApp, InSetIgnoreCaseMutableValue) {
-
-    std::set<std::string> options{"one", "Two", "THREE"};
-    std::string choice;
-    app.add_mutable_set_ignore_case("-q,--quick", choice, options);
-
-    args = {"--quick", "One"};
-    run();
-    EXPECT_EQ("one", choice);
-
-    args = {"--quick", "two"};
-    run();
-    EXPECT_EQ("Two", choice); // Keeps caps from set
-
-    args = {"--quick", "ThrEE"};
-    run();
-    EXPECT_EQ("THREE", choice); // Keeps caps from set
-
-    options.clear();
-    args = {"--quick", "ThrEE"};
-    EXPECT_THROW(run(), CLI::ValidationError);
-}
-
-TEST_F(TApp, InSetIgnoreCasePointer) {
-
-    auto options = std::make_shared<std::set<std::string>>(std::initializer_list<std::string>{"one", "Two", "THREE"});
-    std::string choice;
-    app.add_set_ignore_case("-q,--quick", choice, *options);
-
-    args = {"--quick", "One"};
-    run();
-    EXPECT_EQ("one", choice);
-
-    args = {"--quick", "two"};
-    run();
-    EXPECT_EQ("Two", choice); // Keeps caps from set
-
-    args = {"--quick", "ThrEE"};
-    run();
-    EXPECT_EQ("THREE", choice); // Keeps caps from set
-
-    options.reset();
-    args = {"--quick", "ThrEE"};
-    run();
-    EXPECT_EQ("THREE", choice); // this does not throw a segfault
-
-    args = {"--quick", "four"};
-    EXPECT_THROW(run(), CLI::ValidationError);
-
-    args = {"--quick=one", "--quick=two"};
-    EXPECT_THROW(run(), CLI::ArgumentMismatch);
-}
-
-TEST_F(TApp, InSetIgnoreUnderscore) {
-
-    std::string choice;
-    app.add_set_ignore_underscore("-q,--quick", choice, {"option_one", "option_two", "optionthree"});
-
-    args = {"--quick", "option_one"};
-    run();
-    EXPECT_EQ("option_one", choice);
-
-    args = {"--quick", "optiontwo"};
-    run();
-    EXPECT_EQ("option_two", choice); // Keeps underscore from set
-
-    args = {"--quick", "_option_thr_ee"};
-    run();
-    EXPECT_EQ("optionthree", choice); // no underscore
-
-    args = {"--quick", "Option4"};
-    EXPECT_THROW(run(), CLI::ValidationError);
-
-    args = {"--quick=option_one", "--quick=option_two"};
-    EXPECT_THROW(run(), CLI::ArgumentMismatch);
-}
-
-TEST_F(TApp, InSetIgnoreCaseUnderscore) {
-
-    std::string choice;
-    app.add_set_ignore_case_underscore("-q,--quick", choice, {"Option_One", "option_two", "OptionThree"});
-
-    args = {"--quick", "option_one"};
-    run();
-    EXPECT_EQ("Option_One", choice);
-
-    args = {"--quick", "OptionTwo"};
-    run();
-    EXPECT_EQ("option_two", choice); // Keeps underscore and case from set
-
-    args = {"--quick", "_OPTION_thr_ee"};
-    run();
-    EXPECT_EQ("OptionThree", choice); // no underscore
-
-    args = {"--quick", "Option4"};
-    EXPECT_THROW(run(), CLI::ValidationError);
-
-    args = {"--quick=option_one", "--quick=option_two"};
-    EXPECT_THROW(run(), CLI::ArgumentMismatch);
-}
-
 // #113
 TEST_F(TApp, AddRemoveSetItems) {
     std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
@@ -301,37 +150,6 @@ TEST_F(TApp, AddRemoveSetItems) {
     EXPECT_THROW(run(), CLI::ValidationError);
 }
 
-TEST_F(TApp, AddRemoveSetItemsNoCase) {
-    std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
-
-    std::string type1, type2;
-    app.add_mutable_set_ignore_case("--type1", type1, items);
-    app.add_mutable_set_ignore_case("--type2", type2, items, "", true);
-
-    args = {"--type1", "TYPe1", "--type2", "TyPE2"};
-
-    run();
-    EXPECT_EQ(type1, "TYPE1");
-    EXPECT_EQ(type2, "TYPE2");
-
-    items.insert("TYPE6");
-    items.insert("TYPE7");
-
-    items.erase("TYPE1");
-    items.erase("TYPE2");
-
-    args = {"--type1", "TyPE6", "--type2", "tYPE7"};
-    run();
-    EXPECT_EQ(type1, "TYPE6");
-    EXPECT_EQ(type2, "TYPE7");
-
-    args = {"--type1", "TYPe1"};
-    EXPECT_THROW(run(), CLI::ValidationError);
-
-    args = {"--type2", "TYpE2"};
-    EXPECT_THROW(run(), CLI::ValidationError);
-}
-
 TEST(THelp, Defaults) {
     CLI::App app{"My prog"};
 
@@ -458,7 +276,7 @@ TEST_F(TApp, DefaultedResult) {
     opts->results(nString);
     EXPECT_EQ(nString, "NA");
     int newIval;
-    EXPECT_THROW(optv->results(newIval), CLI::ConversionError);
+    // EXPECT_THROW(optv->results(newIval), CLI::ConversionError);
     optv->default_str("442");
     optv->results(newIval);
     EXPECT_EQ(newIval, 442);
diff --git a/packages/CLI11/tests/FormatterTest.cpp b/packages/CLI11/tests/FormatterTest.cpp
index d3be30168d377fcdf399a3bc0738017d39682ee9..47766a828a6dd2ec00c7160cbc17ec4bc3ac8a3b 100644
--- a/packages/CLI11/tests/FormatterTest.cpp
+++ b/packages/CLI11/tests/FormatterTest.cpp
@@ -49,7 +49,7 @@ TEST(Formatter, OptCustomize) {
     optfmt->label("REQUIRED", "(MUST HAVE)");
     app.formatter(optfmt);
 
-    int v;
+    int v{0};
     app.add_option("--opt", v, "Something")->required();
 
     std::string help = app.help();
@@ -69,7 +69,7 @@ TEST(Formatter, OptCustomizeSimple) {
     app.get_formatter()->column_width(25);
     app.get_formatter()->label("REQUIRED", "(MUST HAVE)");
 
-    int v;
+    int v{0};
     app.add_option("--opt", v, "Something")->required();
 
     std::string help = app.help();
@@ -89,10 +89,10 @@ TEST(Formatter, FalseFlagExample) {
     app.get_formatter()->column_width(25);
     app.get_formatter()->label("REQUIRED", "(MUST HAVE)");
 
-    int v;
+    int v{0};
     app.add_flag("--opt,!--no_opt", v, "Something");
 
-    bool flag;
+    bool flag{false};
     app.add_flag("!-O,--opt2,--no_opt2{false}", flag, "Something else");
 
     std::string help = app.help();
@@ -180,7 +180,7 @@ TEST(Formatter, NamelessSubInGroup) {
     CLI::App *sub = app.add_subcommand("", "This subcommand");
     CLI::App *sub2 = app.add_subcommand("sub2", "subcommand2");
     sub->add_flag("--insub", "MyFlag");
-    int val;
+    int val{0};
     sub2->add_option("pos", val, "positional");
     sub->group("group1");
     sub2->group("group1");
diff --git a/packages/CLI11/tests/HelpTest.cpp b/packages/CLI11/tests/HelpTest.cpp
index 9be442ef19175267488d45d37adf6765aa7e7653..44f0f5318c50f3ebf72e91a34fef78d63c64fdf5 100644
--- a/packages/CLI11/tests/HelpTest.cpp
+++ b/packages/CLI11/tests/HelpTest.cpp
@@ -97,6 +97,141 @@ TEST(THelp, Hidden) {
     EXPECT_THAT(help, Not(HasSubstr("another")));
 }
 
+TEST(THelp, deprecatedOptions) {
+    CLI::App app{"My prog"};
+
+    std::string x;
+    auto soption = app.add_option("--something", x, "My option here");
+    app.add_option("--something_else", x, "My option here");
+    std::string y;
+    app.add_option("--another", y);
+
+    CLI::deprecate_option(soption, "something_else");
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("DEPRECATED"));
+    EXPECT_THAT(help, HasSubstr("something"));
+    EXPECT_NO_THROW(app.parse("--something deprecated"));
+}
+
+TEST(THelp, deprecatedOptions2) {
+    CLI::App app{"My prog"};
+
+    std::string x;
+    app.add_option("--something", x, "My option here");
+    app.add_option("--something_else", x, "My option here");
+    std::string y;
+    app.add_option("--another", y);
+
+    CLI::deprecate_option(&app, "--something");
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("DEPRECATED"));
+    EXPECT_THAT(help, HasSubstr("something"));
+    EXPECT_NO_THROW(app.parse("--something deprecated"));
+}
+
+TEST(THelp, deprecatedOptions3) {
+    CLI::App app{"My prog"};
+
+    std::string x;
+    app.add_option("--something", x, "Some Description");
+    app.add_option("--something_else", x, "Some other description");
+    std::string y;
+    app.add_option("--another", y);
+
+    CLI::deprecate_option(app, "--something", "--something_else");
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("DEPRECATED"));
+    EXPECT_THAT(help, HasSubstr("'--something_else' instead"));
+    EXPECT_NO_THROW(app.parse("--something deprecated"));
+}
+
+TEST(THelp, retiredOptions) {
+    CLI::App app{"My prog"};
+
+    std::string x;
+    auto opt1 = app.add_option("--something", x, "My option here");
+    app.add_option("--something_else", x, "My option here");
+    std::string y;
+    app.add_option("--another", y);
+
+    CLI::retire_option(app, opt1);
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("RETIRED"));
+    EXPECT_THAT(help, HasSubstr("something"));
+
+    EXPECT_NO_THROW(app.parse("--something old"));
+}
+
+TEST(THelp, retiredOptions2) {
+    CLI::App app{"My prog"};
+
+    std::string x;
+    app.add_option("--something_else", x, "My option here");
+    std::string y;
+    app.add_option("--another", y);
+
+    CLI::retire_option(&app, "--something");
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("RETIRED"));
+    EXPECT_THAT(help, HasSubstr("something"));
+    EXPECT_NO_THROW(app.parse("--something old"));
+}
+
+TEST(THelp, retiredOptions3) {
+    CLI::App app{"My prog"};
+
+    std::string x;
+    app.add_option("--something", x, "My option here");
+    app.add_option("--something_else", x, "My option here");
+    std::string y;
+    app.add_option("--another", y);
+
+    CLI::retire_option(app, "--something");
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("RETIRED"));
+    EXPECT_THAT(help, HasSubstr("something"));
+
+    EXPECT_NO_THROW(app.parse("--something old"));
+}
+
+TEST(THelp, HiddenGroup) {
+    CLI::App app{"My prog"};
+    // empty option group name should be hidden
+    auto hgroup = app.add_option_group("");
+    std::string x;
+    hgroup->add_option("something", x, "My option here");
+    std::string y;
+    hgroup->add_option("--another", y);
+
+    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, Not(HasSubstr("[something]")));
+    EXPECT_THAT(help, Not(HasSubstr("something ")));
+    EXPECT_THAT(help, Not(HasSubstr("another")));
+
+    hgroup->group("ghidden");
+
+    help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("something "));
+    EXPECT_THAT(help, HasSubstr("another"));
+}
+
 TEST(THelp, OptionalPositionalAndOptions) {
     CLI::App app{"My prog", "AnotherProgram"};
     app.add_flag("-q,--quick");
@@ -195,7 +330,7 @@ TEST(THelp, Needs) {
 TEST(THelp, NeedsPositional) {
     CLI::App app{"My prog"};
 
-    int x, y;
+    int x{0}, y{0};
 
     CLI::Option *op1 = app.add_option("op1", x, "one");
     app.add_option("op2", y, "two")->needs(op1);
@@ -220,7 +355,7 @@ TEST(THelp, Excludes) {
 TEST(THelp, ExcludesPositional) {
     CLI::App app{"My prog"};
 
-    int x, y;
+    int x{0}, y{0};
 
     CLI::Option *op1 = app.add_option("op1", x);
     app.add_option("op2", y)->excludes(op1);
@@ -246,7 +381,7 @@ TEST(THelp, ManualSetters) {
 
     CLI::App app{"My prog"};
 
-    int x = 1;
+    int x{1};
 
     CLI::Option *op1 = app.add_option("--op", x);
     op1->default_str("12");
@@ -262,13 +397,28 @@ TEST(THelp, ManualSetters) {
     EXPECT_EQ(x, 14);
     help = app.help();
     EXPECT_THAT(help, HasSubstr("=14"));
+
+    op1->default_val(12);
+    EXPECT_EQ(x, 12);
+    help = app.help();
+    EXPECT_THAT(help, HasSubstr("=12"));
+
+    EXPECT_TRUE(op1->get_run_callback_for_default());
+    op1->run_callback_for_default(false);
+    EXPECT_FALSE(op1->get_run_callback_for_default());
+
+    op1->default_val(18);
+    // x should not be modified in this case
+    EXPECT_EQ(x, 12);
+    help = app.help();
+    EXPECT_THAT(help, HasSubstr("=18"));
 }
 
 TEST(THelp, ManualSetterOverFunction) {
 
     CLI::App app{"My prog"};
 
-    int x = 1;
+    int x{1};
 
     CLI::Option *op1 = app.add_option("--op1", x)->check(CLI::IsMember({1, 2}));
     CLI::Option *op2 = app.add_option("--op2", x)->transform(CLI::IsMember({1, 2}));
@@ -500,7 +650,7 @@ TEST(THelp, CustomHelp) {
 
 TEST(THelp, NextLineShouldBeAlignmentInMultilineDescription) {
     CLI::App app;
-    int i;
+    int i{0};
     const std::string first{"first line"};
     const std::string second{"second line"};
     app.add_option("-i,--int", i, first + "\n" + second);
@@ -513,7 +663,7 @@ TEST(THelp, NextLineShouldBeAlignmentInMultilineDescription) {
 TEST(THelp, NiceName) {
     CLI::App app;
 
-    int x;
+    int x{0};
     auto long_name = app.add_option("-s,--long,-q,--other,that", x);
     auto short_name = app.add_option("more,-x,-y", x);
     auto positional = app.add_option("posit", x);
@@ -570,8 +720,8 @@ TEST(Exit, ExitCodes) {
 
 struct CapturedHelp : public ::testing::Test {
     CLI::App app{"My Test Program"};
-    std::stringstream out;
-    std::stringstream err;
+    std::stringstream out{};
+    std::stringstream err{};
 
     int run(const CLI::Error &e) { return app.exit(e, out, err); }
 
@@ -728,7 +878,7 @@ TEST(THelp, SetDescriptionAfterCreation) {
 TEST(THelp, AccessOptionDescription) {
     CLI::App app{};
 
-    int x;
+    int x{0};
     auto opt = app.add_option("-a,--alpha", x, "My description goes here");
 
     EXPECT_EQ(opt->get_description(), "My description goes here");
@@ -737,7 +887,7 @@ TEST(THelp, AccessOptionDescription) {
 TEST(THelp, SetOptionDescriptionAfterCreation) {
     CLI::App app{};
 
-    int x;
+    int x{0};
     auto opt = app.add_option("-a,--alpha", x);
     opt->description("My description goes here");
 
@@ -748,7 +898,7 @@ TEST(THelp, SetOptionDescriptionAfterCreation) {
 TEST(THelp, CleanNeeds) {
     CLI::App app;
 
-    int x;
+    int x{0};
     auto a_name = app.add_option("-a,--alpha", x);
     app.add_option("-b,--boo", x)->needs(a_name);
 
@@ -760,7 +910,7 @@ TEST(THelp, CleanNeeds) {
 TEST(THelp, RequiredPrintout) {
     CLI::App app;
 
-    int x;
+    int x{0};
     app.add_option("-a,--alpha", x)->required();
 
     EXPECT_THAT(app.help(), HasSubstr(" REQUIRED"));
@@ -786,8 +936,8 @@ TEST(THelp, ValidatorsText) {
     CLI::App app;
 
     std::string filename;
-    int x;
-    unsigned int y;
+    int x{0};
+    unsigned int y{0};
     app.add_option("--f1", filename)->check(CLI::ExistingFile);
     app.add_option("--f3", x)->check(CLI::Range(1, 4));
     app.add_option("--f4", y)->check(CLI::Range(12));
@@ -882,7 +1032,7 @@ TEST(THelp, ChangingSet) {
     CLI::App app;
 
     std::set<int> vals{1, 2, 3};
-    int val;
+    int val{0};
     app.add_option("--val", val)->check(CLI::IsMember(&vals));
 
     std::string help = app.help();
@@ -903,7 +1053,7 @@ TEST(THelp, ChangingSetDefaulted) {
     CLI::App app;
 
     std::set<int> vals{1, 2, 3};
-    int val = 2;
+    int val{2};
     app.add_option("--val", val, "")->check(CLI::IsMember(&vals))->capture_default_str();
 
     std::string help = app.help();
diff --git a/packages/CLI11/tests/HelpersTest.cpp b/packages/CLI11/tests/HelpersTest.cpp
index 778046c9f8e2382010e9713d4d2d01f27f5f49c2..9b4c288337ed5008fabca70a587743912d6bd5b9 100644
--- a/packages/CLI11/tests/HelpersTest.cpp
+++ b/packages/CLI11/tests/HelpersTest.cpp
@@ -8,6 +8,7 @@
 #include <fstream>
 #include <string>
 #include <tuple>
+#include <utility>
 
 class NotStreamable {};
 
@@ -42,13 +43,32 @@ TEST(TypeTools, type_size) {
     V = CLI::detail::type_count<void>::value;
     EXPECT_EQ(V, 0);
     V = CLI::detail::type_count<std::vector<double>>::value;
-    EXPECT_EQ(V, -1);
+    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);
+    V = CLI::detail::type_count<std::vector<std::pair<std::string, double>>>::value;
+    EXPECT_EQ(V, 2);
+}
+
+TEST(TypeTools, expected_count) {
+    auto V = CLI::detail::expected_count<int>::value;
+    EXPECT_EQ(V, 1);
+    V = CLI::detail::expected_count<void>::value;
+    EXPECT_EQ(V, 0);
+    V = CLI::detail::expected_count<std::vector<double>>::value;
+    EXPECT_EQ(V, CLI::detail::expected_max_vector_size);
+    V = CLI::detail::expected_count<std::tuple<double, int>>::value;
+    EXPECT_EQ(V, 1);
+    V = CLI::detail::expected_count<std::tuple<std::string, double, int>>::value;
+    EXPECT_EQ(V, 1);
+    V = CLI::detail::expected_count<std::array<std::string, 5>>::value;
+    EXPECT_EQ(V, 1);
+    V = CLI::detail::expected_count<std::vector<std::pair<std::string, double>>>::value;
+    EXPECT_EQ(V, CLI::detail::expected_max_vector_size);
 }
 
 TEST(Split, SimpleByToken) {
@@ -85,8 +105,8 @@ TEST(String, InvalidName) {
 }
 
 TEST(StringTools, Modify) {
-    int cnt = 0;
-    std::string newString = CLI::detail::find_and_modify("======", "=", [&cnt](std::string &str, size_t index) {
+    int cnt{0};
+    std::string newString = CLI::detail::find_and_modify("======", "=", [&cnt](std::string &str, std::size_t index) {
         if((++cnt) % 2 == 0) {
             str[index] = ':';
         }
@@ -97,7 +117,7 @@ TEST(StringTools, Modify) {
 
 TEST(StringTools, Modify2) {
     std::string newString =
-        CLI::detail::find_and_modify("this is a string test", "is", [](std::string &str, size_t index) {
+        CLI::detail::find_and_modify("this is a string test", "is", [](std::string &str, std::size_t index) {
             if((index > 1) && (str[index - 1] != ' ')) {
                 str[index] = 'a';
                 str[index + 1] = 't';
@@ -109,7 +129,7 @@ TEST(StringTools, Modify2) {
 
 TEST(StringTools, Modify3) {
     // this picks up 3 sets of 3 after the 'b' then collapses the new first set
-    std::string newString = CLI::detail::find_and_modify("baaaaaaaaaa", "aaa", [](std::string &str, size_t index) {
+    std::string newString = CLI::detail::find_and_modify("baaaaaaaaaa", "aaa", [](std::string &str, std::size_t index) {
         str.erase(index, 3);
         str.insert(str.begin(), 'a');
         return 0u;
@@ -277,6 +297,8 @@ TEST(Validators, IPValidate1) {
     EXPECT_FALSE(CLI::ValidIPV4(ip).empty());
     ip = "aaa";
     EXPECT_FALSE(CLI::ValidIPV4(ip).empty());
+    ip = "1.2.3.abc";
+    EXPECT_FALSE(CLI::ValidIPV4(ip).empty());
     ip = "11.22";
     EXPECT_FALSE(CLI::ValidIPV4(ip).empty());
 }
@@ -289,13 +311,36 @@ TEST(Validators, PositiveValidator) {
     num = "10000";
     EXPECT_TRUE(CLI::PositiveNumber(num).empty());
     num = "0";
+    EXPECT_FALSE(CLI::PositiveNumber(num).empty());
+    num = "+0.5";
     EXPECT_TRUE(CLI::PositiveNumber(num).empty());
     num = "-1";
     EXPECT_FALSE(CLI::PositiveNumber(num).empty());
+    num = "-1.5";
+    EXPECT_FALSE(CLI::PositiveNumber(num).empty());
     num = "a";
     EXPECT_FALSE(CLI::PositiveNumber(num).empty());
 }
 
+TEST(Validators, NonNegativeValidator) {
+    std::string num = "1.1.1.1";
+    EXPECT_FALSE(CLI::NonNegativeNumber(num).empty());
+    num = "1";
+    EXPECT_TRUE(CLI::NonNegativeNumber(num).empty());
+    num = "10000";
+    EXPECT_TRUE(CLI::NonNegativeNumber(num).empty());
+    num = "0";
+    EXPECT_TRUE(CLI::NonNegativeNumber(num).empty());
+    num = "+0.5";
+    EXPECT_TRUE(CLI::NonNegativeNumber(num).empty());
+    num = "-1";
+    EXPECT_FALSE(CLI::NonNegativeNumber(num).empty());
+    num = "-1.5";
+    EXPECT_FALSE(CLI::NonNegativeNumber(num).empty());
+    num = "a";
+    EXPECT_FALSE(CLI::NonNegativeNumber(num).empty());
+}
+
 TEST(Validators, NumberValidator) {
     std::string num = "1.1.1.1";
     EXPECT_FALSE(CLI::Number(num).empty());
@@ -399,8 +444,8 @@ TEST(Validators, ProgramNameSplit) {
 }
 
 TEST(CheckedMultiply, Int) {
-    int a = 10;
-    int b = -20;
+    int a{10};
+    int b{-20};
     ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
     ASSERT_EQ(a, -200);
 
@@ -480,8 +525,8 @@ TEST(CheckedMultiply, Int) {
 }
 
 TEST(CheckedMultiply, SizeT) {
-    size_t a = 10;
-    size_t b = 20;
+    std::size_t a = 10;
+    std::size_t b = 20;
     ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
     ASSERT_EQ(a, 200u);
 
@@ -495,77 +540,77 @@ TEST(CheckedMultiply, SizeT) {
     ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
     ASSERT_EQ(a, 0u);
 
-    a = std::numeric_limits<size_t>::max();
+    a = std::numeric_limits<std::size_t>::max();
     b = 1u;
     ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
-    ASSERT_EQ(a, std::numeric_limits<size_t>::max());
+    ASSERT_EQ(a, std::numeric_limits<std::size_t>::max());
 
-    a = std::numeric_limits<size_t>::max();
+    a = std::numeric_limits<std::size_t>::max();
     b = 2u;
     ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
-    ASSERT_EQ(a, std::numeric_limits<size_t>::max());
+    ASSERT_EQ(a, std::numeric_limits<std::size_t>::max());
 
-    a = std::numeric_limits<size_t>::max();
-    b = std::numeric_limits<size_t>::max();
+    a = std::numeric_limits<std::size_t>::max();
+    b = std::numeric_limits<std::size_t>::max();
     ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
-    ASSERT_EQ(a, std::numeric_limits<size_t>::max());
+    ASSERT_EQ(a, std::numeric_limits<std::size_t>::max());
 
-    a = std::numeric_limits<size_t>::max() / 100;
+    a = std::numeric_limits<std::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);
+    ASSERT_EQ(a, std::numeric_limits<std::size_t>::max() / 100u * 99u);
 }
 
 TEST(CheckedMultiply, Float) {
-    float a = 10;
-    float b = 20;
+    float a{10.0F};
+    float b{20.0F};
     ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
     ASSERT_FLOAT_EQ(a, 200);
 
-    a = 0;
-    b = 20;
+    a = 0.0F;
+    b = 20.0F;
     ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
     ASSERT_FLOAT_EQ(a, 0);
 
     a = INFINITY;
-    b = 20;
+    b = 20.0F;
     ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
     ASSERT_FLOAT_EQ(a, INFINITY);
 
-    a = 2;
+    a = 2.0F;
     b = -INFINITY;
     ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
     ASSERT_FLOAT_EQ(a, -INFINITY);
 
-    a = std::numeric_limits<float>::max() / 100;
-    b = 1;
+    a = std::numeric_limits<float>::max() / 100.0F;
+    b = 1.0F;
     ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
-    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100);
+    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100.0F);
 
-    a = std::numeric_limits<float>::max() / 100;
-    b = 99;
+    a = std::numeric_limits<float>::max() / 100.0F;
+    b = 99.0F;
     ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
-    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100 * 99);
+    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100.0F * 99.0F);
 
-    a = std::numeric_limits<float>::max() / 100;
+    a = std::numeric_limits<float>::max() / 100.0F;
     b = 101;
     ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
-    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100);
+    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100.0F);
 
-    a = std::numeric_limits<float>::max() / 100;
+    a = std::numeric_limits<float>::max() / 100.0F;
     b = -99;
     ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
-    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100 * -99);
+    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100.0F * -99.0F);
 
-    a = std::numeric_limits<float>::max() / 100;
+    a = std::numeric_limits<float>::max() / 100.0F;
     b = -101;
     ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
-    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100);
+    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100.0F);
 }
 
 TEST(CheckedMultiply, Double) {
-    double a = 10;
-    double b = 20;
+    double a{10.0F};
+    double b{20.0F};
     ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
     ASSERT_DOUBLE_EQ(a, 200);
 
@@ -813,16 +858,27 @@ TEST(Types, TypeName) {
     vector_name = CLI::detail::type_name<std::vector<double>>();
     EXPECT_EQ("FLOAT", vector_name);
 
+    static_assert(CLI::detail::classify_object<std::pair<int, std::string>>::value ==
+                      CLI::detail::object_category::tuple_value,
+                  "pair<int,string> does not read like a tuple");
+
+    std::string pair_name = CLI::detail::type_name<std::vector<std::pair<int, std::string>>>();
+    EXPECT_EQ("[INT,TEXT]", pair_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);
+
+    auto vclass = CLI::detail::classify_object<std::vector<std::vector<unsigned char>>>::value;
+    EXPECT_EQ(vclass, CLI::detail::object_category::vector_value);
+
+    auto tclass = CLI::detail::classify_object<std::tuple<double>>::value;
+    EXPECT_EQ(tclass, CLI::detail::object_category::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,
+                      CLI::detail::object_category::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);
@@ -850,8 +906,8 @@ TEST(Types, TypeName) {
     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,
+    EXPECT_EQ(vclass, CLI::detail::object_category::tuple_value);
+    static_assert(CLI::detail::classify_object<std::tuple<test>>::value == CLI::detail::object_category::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);
@@ -957,8 +1013,8 @@ TEST(Types, LexicalCastEnum) {
     EXPECT_EQ(output, v5);
 
     EXPECT_FALSE(CLI::detail::lexical_cast("invalid", output));
-    enum class t2 : uint64_t { enum1 = 65, enum2 = 45667, enum3 = 9999999999999 };
-    t2 output2;
+    enum class t2 : std::uint64_t { enum1 = 65, enum2 = 45667, enum3 = 9999999999999 };
+    t2 output2{t2::enum2};
     EXPECT_TRUE(CLI::detail::lexical_cast("65", output2));
     EXPECT_EQ(output2, t2::enum1);
 
@@ -970,7 +1026,7 @@ TEST(Types, LexicalCastEnum) {
 
 TEST(Types, LexicalConversionDouble) {
     CLI::results_t input = {"9.12"};
-    long double x;
+    long double x{0.0};
     bool res = CLI::detail::lexical_conversion<long double, double>(input, x);
     EXPECT_TRUE(res);
     EXPECT_FLOAT_EQ((float)9.12, (float)x);
@@ -982,7 +1038,7 @@ TEST(Types, LexicalConversionDouble) {
 
 TEST(Types, LexicalConversionDoubleTuple) {
     CLI::results_t input = {"9.12"};
-    std::tuple<double> x;
+    std::tuple<double> x{0.0};
     bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
     EXPECT_TRUE(res);
     EXPECT_DOUBLE_EQ(9.12, std::get<0>(x));
@@ -1017,7 +1073,7 @@ static_assert(CLI::detail::is_tuple_like<std::tuple<double, int, double>>::value
 TEST(Types, LexicalConversionTuple2) {
     CLI::results_t input = {"9.12", "19"};
 
-    std::tuple<double, int> x;
+    std::tuple<double, int> x{0.0, 0};
     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);
diff --git a/packages/CLI11/tests/IniTest.cpp b/packages/CLI11/tests/IniTest.cpp
deleted file mode 100644
index afff26bcdb0554775a8f59c9dd3044a4fbb7ae49..0000000000000000000000000000000000000000
--- a/packages/CLI11/tests/IniTest.cpp
+++ /dev/null
@@ -1,913 +0,0 @@
-#include "app_helper.hpp"
-
-#include "gmock/gmock.h"
-#include <cstdio>
-#include <sstream>
-
-using ::testing::HasSubstr;
-using ::testing::Not;
-
-TEST(StringBased, IniJoin) {
-    std::vector<std::string> items = {"one", "two", "three four"};
-    std::string result = "one two \"three four\"";
-
-    EXPECT_EQ(CLI::detail::ini_join(items), result);
-}
-
-TEST(StringBased, First) {
-    std::stringstream ofile;
-
-    ofile << "one=three" << std::endl;
-    ofile << "two=four" << std::endl;
-
-    ofile.seekg(0, std::ios::beg);
-
-    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
-
-    EXPECT_EQ(2u, output.size());
-    EXPECT_EQ("one", output.at(0).name);
-    EXPECT_EQ(1u, output.at(0).inputs.size());
-    EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name);
-    EXPECT_EQ(1u, output.at(1).inputs.size());
-    EXPECT_EQ("four", output.at(1).inputs.at(0));
-}
-
-TEST(StringBased, FirstWithComments) {
-    std::stringstream ofile;
-
-    ofile << ";this is a comment" << std::endl;
-    ofile << "one=three" << std::endl;
-    ofile << "two=four" << std::endl;
-    ofile << "; and another one" << std::endl;
-
-    ofile.seekg(0, std::ios::beg);
-
-    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
-
-    EXPECT_EQ(2u, output.size());
-    EXPECT_EQ("one", output.at(0).name);
-    EXPECT_EQ(1u, output.at(0).inputs.size());
-    EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name);
-    EXPECT_EQ(1u, output.at(1).inputs.size());
-    EXPECT_EQ("four", output.at(1).inputs.at(0));
-}
-
-TEST(StringBased, Quotes) {
-    std::stringstream ofile;
-
-    ofile << R"(one = "three")" << std::endl;
-    ofile << R"(two = 'four')" << std::endl;
-    ofile << R"(five = "six and seven")" << std::endl;
-
-    ofile.seekg(0, std::ios::beg);
-
-    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
-
-    EXPECT_EQ(3u, output.size());
-    EXPECT_EQ("one", output.at(0).name);
-    EXPECT_EQ(1u, output.at(0).inputs.size());
-    EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name);
-    EXPECT_EQ(1u, output.at(1).inputs.size());
-    EXPECT_EQ("four", output.at(1).inputs.at(0));
-    EXPECT_EQ("five", output.at(2).name);
-    EXPECT_EQ(1u, output.at(2).inputs.size());
-    EXPECT_EQ("six and seven", output.at(2).inputs.at(0));
-}
-
-TEST(StringBased, Vector) {
-    std::stringstream ofile;
-
-    ofile << "one = three" << std::endl;
-    ofile << "two = four" << std::endl;
-    ofile << "five = six and seven" << std::endl;
-
-    ofile.seekg(0, std::ios::beg);
-
-    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
-
-    EXPECT_EQ(3u, output.size());
-    EXPECT_EQ("one", output.at(0).name);
-    EXPECT_EQ(1u, output.at(0).inputs.size());
-    EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name);
-    EXPECT_EQ(1u, output.at(1).inputs.size());
-    EXPECT_EQ("four", output.at(1).inputs.at(0));
-    EXPECT_EQ("five", output.at(2).name);
-    EXPECT_EQ(3u, output.at(2).inputs.size());
-    EXPECT_EQ("six", output.at(2).inputs.at(0));
-    EXPECT_EQ("and", output.at(2).inputs.at(1));
-    EXPECT_EQ("seven", output.at(2).inputs.at(2));
-}
-
-TEST(StringBased, Spaces) {
-    std::stringstream ofile;
-
-    ofile << "one = three" << std::endl;
-    ofile << "two = four" << std::endl;
-
-    ofile.seekg(0, std::ios::beg);
-
-    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
-
-    EXPECT_EQ(2u, output.size());
-    EXPECT_EQ("one", output.at(0).name);
-    EXPECT_EQ(1u, output.at(0).inputs.size());
-    EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name);
-    EXPECT_EQ(1u, output.at(1).inputs.size());
-    EXPECT_EQ("four", output.at(1).inputs.at(0));
-}
-
-TEST(StringBased, Sections) {
-    std::stringstream ofile;
-
-    ofile << "one=three" << std::endl;
-    ofile << "[second]" << std::endl;
-    ofile << "  two=four" << std::endl;
-
-    ofile.seekg(0, std::ios::beg);
-
-    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
-
-    EXPECT_EQ(2u, output.size());
-    EXPECT_EQ("one", output.at(0).name);
-    EXPECT_EQ(1u, output.at(0).inputs.size());
-    EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name);
-    EXPECT_EQ("second", output.at(1).parents.at(0));
-    EXPECT_EQ(1u, output.at(1).inputs.size());
-    EXPECT_EQ("four", output.at(1).inputs.at(0));
-    EXPECT_EQ("second.two", output.at(1).fullname());
-}
-
-TEST(StringBased, SpacesSections) {
-    std::stringstream ofile;
-
-    ofile << "one=three" << std::endl;
-    ofile << std::endl;
-    ofile << "[second]" << std::endl;
-    ofile << "   " << std::endl;
-    ofile << "  two=four" << std::endl;
-
-    ofile.seekg(0, std::ios::beg);
-
-    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
-
-    EXPECT_EQ(2u, output.size());
-    EXPECT_EQ("one", output.at(0).name);
-    EXPECT_EQ(1u, output.at(0).inputs.size());
-    EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name);
-    EXPECT_EQ(1u, output.at(1).parents.size());
-    EXPECT_EQ("second", output.at(1).parents.at(0));
-    EXPECT_EQ(1u, output.at(1).inputs.size());
-    EXPECT_EQ("four", output.at(1).inputs.at(0));
-}
-
-TEST_F(TApp, IniNotRequired) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "two=99" << std::endl;
-        out << "three=3" << std::endl;
-    }
-
-    int one = 0, two = 0, three = 0;
-    app.add_option("--one", one);
-    app.add_option("--two", two);
-    app.add_option("--three", three);
-
-    args = {"--one=1"};
-
-    run();
-
-    EXPECT_EQ(1, one);
-    EXPECT_EQ(99, two);
-    EXPECT_EQ(3, three);
-
-    one = two = three = 0;
-    args = {"--one=1", "--two=2"};
-
-    run();
-
-    EXPECT_EQ(1, one);
-    EXPECT_EQ(2, two);
-    EXPECT_EQ(3, three);
-}
-
-TEST_F(TApp, IniSuccessOnUnknownOption) {
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    app.set_config("--config", tmpini);
-    app.allow_config_extras(true);
-
-    {
-        std::ofstream out{tmpini};
-        out << "three=3" << std::endl;
-        out << "two=99" << std::endl;
-    }
-
-    int two = 0;
-    app.add_option("--two", two);
-    run();
-    EXPECT_EQ(99, two);
-}
-
-TEST_F(TApp, IniGetRemainingOption) {
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    app.set_config("--config", tmpini);
-    app.allow_config_extras(true);
-
-    std::string ExtraOption = "three";
-    std::string ExtraOptionValue = "3";
-    {
-        std::ofstream out{tmpini};
-        out << ExtraOption << "=" << ExtraOptionValue << std::endl;
-        out << "two=99" << std::endl;
-    }
-
-    int two = 0;
-    app.add_option("--two", two);
-    ASSERT_NO_THROW(run());
-    std::vector<std::string> ExpectedRemaining = {ExtraOption};
-    EXPECT_EQ(app.remaining(), ExpectedRemaining);
-}
-
-TEST_F(TApp, IniGetNoRemaining) {
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    app.set_config("--config", tmpini);
-    app.allow_config_extras(true);
-
-    {
-        std::ofstream out{tmpini};
-        out << "two=99" << std::endl;
-    }
-
-    int two = 0;
-    app.add_option("--two", two);
-    ASSERT_NO_THROW(run());
-    EXPECT_EQ(app.remaining().size(), 0u);
-}
-
-TEST_F(TApp, IniNotRequiredNotDefault) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-    TempFile tmpini2{"TestIniTmp2.ini"};
-
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "two=99" << std::endl;
-        out << "three=3" << std::endl;
-    }
-
-    {
-        std::ofstream out{tmpini2};
-        out << "[default]" << std::endl;
-        out << "two=98" << std::endl;
-        out << "three=4" << std::endl;
-    }
-
-    int one = 0, two = 0, three = 0;
-    app.add_option("--one", one);
-    app.add_option("--two", two);
-    app.add_option("--three", three);
-
-    run();
-
-    EXPECT_EQ(99, two);
-    EXPECT_EQ(3, three);
-
-    args = {"--config", tmpini2};
-    run();
-
-    EXPECT_EQ(98, two);
-    EXPECT_EQ(4, three);
-}
-
-TEST_F(TApp, IniRequiredNotFound) {
-
-    std::string noini = "TestIniNotExist.ini";
-    app.set_config("--config", noini, "", true);
-
-    EXPECT_THROW(run(), CLI::FileError);
-}
-
-TEST_F(TApp, IniNotRequiredPassedNotFound) {
-
-    std::string noini = "TestIniNotExist.ini";
-    app.set_config("--config", "", "", false);
-
-    args = {"--config", noini};
-    EXPECT_THROW(run(), CLI::FileError);
-}
-
-TEST_F(TApp, IniOverwrite) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "two=99" << std::endl;
-    }
-
-    std::string orig = "filename_not_exist.ini";
-    std::string next = "TestIniTmp.ini";
-    app.set_config("--config", orig);
-    // Make sure this can be overwritten
-    app.set_config("--conf", next);
-    int two = 7;
-    app.add_option("--two", two);
-
-    run();
-
-    EXPECT_EQ(99, two);
-}
-
-TEST_F(TApp, IniRequired) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    app.set_config("--config", tmpini, "", true);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "two=99" << std::endl;
-        out << "three=3" << std::endl;
-    }
-
-    int one = 0, two = 0, three = 0;
-    app.add_option("--one", one)->required();
-    app.add_option("--two", two)->required();
-    app.add_option("--three", three)->required();
-
-    args = {"--one=1"};
-
-    run();
-
-    one = two = three = 0;
-    args = {"--one=1", "--two=2"};
-
-    run();
-
-    args = {};
-
-    EXPECT_THROW(run(), CLI::RequiredError);
-
-    args = {"--two=2"};
-
-    EXPECT_THROW(run(), CLI::RequiredError);
-}
-
-TEST_F(TApp, IniVector) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "two=2 3" << std::endl;
-        out << "three=1 2 3" << std::endl;
-    }
-
-    std::vector<int> two, three;
-    app.add_option("--two", two)->expected(2)->required();
-    app.add_option("--three", three)->required();
-
-    run();
-
-    EXPECT_EQ(std::vector<int>({2, 3}), two);
-    EXPECT_EQ(std::vector<int>({1, 2, 3}), three);
-}
-
-TEST_F(TApp, IniLayered) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "val=1" << std::endl;
-        out << "[subcom]" << std::endl;
-        out << "val=2" << std::endl;
-        out << "subsubcom.val=3" << std::endl;
-    }
-
-    int one = 0, two = 0, three = 0;
-    app.add_option("--val", one);
-    auto subcom = app.add_subcommand("subcom");
-    subcom->add_option("--val", two);
-    auto subsubcom = subcom->add_subcommand("subsubcom");
-    subsubcom->add_option("--val", three);
-
-    run();
-
-    EXPECT_EQ(1, one);
-    EXPECT_EQ(2, two);
-    EXPECT_EQ(3, three);
-}
-
-TEST_F(TApp, IniFailure) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "val=1" << std::endl;
-    }
-
-    EXPECT_THROW(run(), CLI::ConfigError);
-}
-
-TEST_F(TApp, IniConfigurable) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    app.set_config("--config", tmpini);
-    bool value;
-    app.add_flag("--val", value)->configurable(true);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "val=1" << std::endl;
-    }
-
-    ASSERT_NO_THROW(run());
-    EXPECT_TRUE(value);
-}
-
-TEST_F(TApp, IniNotConfigurable) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    app.set_config("--config", tmpini);
-    bool value;
-    app.add_flag("--val", value)->configurable(false);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "val=1" << std::endl;
-    }
-
-    EXPECT_THROW(run(), CLI::ConfigError);
-}
-
-TEST_F(TApp, IniSubFailure) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    app.add_subcommand("other");
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[other]" << std::endl;
-        out << "val=1" << std::endl;
-    }
-
-    EXPECT_THROW(run(), CLI::ConfigError);
-}
-
-TEST_F(TApp, IniNoSubFailure) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[other]" << std::endl;
-        out << "val=1" << std::endl;
-    }
-
-    EXPECT_THROW(run(), CLI::ConfigError);
-}
-
-TEST_F(TApp, IniFlagConvertFailure) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    app.add_flag("--flag");
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "flag=moobook" << std::endl;
-    }
-    run();
-    bool result;
-    auto *opt = app.get_option("--flag");
-    EXPECT_THROW(opt->results(result), CLI::ConversionError);
-    std::string res;
-    opt->results(res);
-    EXPECT_EQ(res, "moobook");
-}
-
-TEST_F(TApp, IniFlagNumbers) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    bool boo;
-    app.add_flag("--flag", boo);
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "flag=3" << std::endl;
-    }
-
-    ASSERT_NO_THROW(run());
-    EXPECT_TRUE(boo);
-}
-
-TEST_F(TApp, IniFlagDual) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    bool boo;
-    app.add_flag("--flag", boo);
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "flag=1 1" << std::endl;
-    }
-
-    EXPECT_THROW(run(), CLI::ConversionError);
-}
-
-TEST_F(TApp, IniFlagText) {
-
-    TempFile tmpini{"TestIniTmp.ini"};
-
-    bool flag1, flag2, flag3, flag4;
-    app.add_flag("--flag1", flag1);
-    app.add_flag("--flag2", flag2);
-    app.add_flag("--flag3", flag3);
-    app.add_flag("--flag4", flag4);
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "flag1=true" << std::endl;
-        out << "flag2=on" << std::endl;
-        out << "flag3=off" << std::endl;
-        out << "flag4=1" << std::endl;
-    }
-
-    run();
-
-    EXPECT_TRUE(flag1);
-    EXPECT_TRUE(flag2);
-    EXPECT_FALSE(flag3);
-    EXPECT_TRUE(flag4);
-}
-
-TEST_F(TApp, IniFlags) {
-    TempFile tmpini{"TestIniTmp.ini"};
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "two=2" << std::endl;
-        out << "three=true" << std::endl;
-        out << "four=on" << std::endl;
-        out << "five" << std::endl;
-    }
-
-    int two;
-    bool three, four, five;
-    app.add_flag("--two", two);
-    app.add_flag("--three", three);
-    app.add_flag("--four", four);
-    app.add_flag("--five", five);
-
-    run();
-
-    EXPECT_EQ(2, two);
-    EXPECT_TRUE(three);
-    EXPECT_TRUE(four);
-    EXPECT_TRUE(five);
-}
-
-TEST_F(TApp, IniFalseFlags) {
-    TempFile tmpini{"TestIniTmp.ini"};
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "two=-2" << std::endl;
-        out << "three=false" << std::endl;
-        out << "four=1" << std::endl;
-        out << "five" << std::endl;
-    }
-
-    int two;
-    bool three, four, five;
-    app.add_flag("--two", two);
-    app.add_flag("--three", three);
-    app.add_flag("--four", four);
-    app.add_flag("--five", five);
-
-    run();
-
-    EXPECT_EQ(-2, two);
-    EXPECT_FALSE(three);
-    EXPECT_TRUE(four);
-    EXPECT_TRUE(five);
-}
-
-TEST_F(TApp, IniFalseFlagsDef) {
-    TempFile tmpini{"TestIniTmp.ini"};
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "two=2" << std::endl;
-        out << "three=true" << std::endl;
-        out << "four=on" << std::endl;
-        out << "five" << std::endl;
-    }
-
-    int two;
-    bool three, four, five;
-    app.add_flag("--two{false}", two);
-    app.add_flag("--three", three);
-    app.add_flag("!--four", four);
-    app.add_flag("--five", five);
-
-    run();
-
-    EXPECT_EQ(-2, two);
-    EXPECT_TRUE(three);
-    EXPECT_FALSE(four);
-    EXPECT_TRUE(five);
-}
-
-TEST_F(TApp, IniFalseFlagsDefDisableOverrideError) {
-    TempFile tmpini{"TestIniTmp.ini"};
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "two=2" << std::endl;
-        out << "four=on" << std::endl;
-        out << "five" << std::endl;
-    }
-
-    int two;
-    bool four, five;
-    app.add_flag("--two{false}", two)->disable_flag_override();
-    app.add_flag("!--four", four);
-    app.add_flag("--five", five);
-
-    EXPECT_THROW(run(), CLI::ArgumentMismatch);
-}
-
-TEST_F(TApp, IniFalseFlagsDefDisableOverrideSuccess) {
-    TempFile tmpini{"TestIniTmp.ini"};
-    app.set_config("--config", tmpini);
-
-    {
-        std::ofstream out{tmpini};
-        out << "[default]" << std::endl;
-        out << "two=2" << std::endl;
-        out << "four={}" << std::endl;
-        out << "val=15" << std::endl;
-    }
-
-    int two, four, val;
-    app.add_flag("--two{2}", two)->disable_flag_override();
-    app.add_flag("--four{4}", four)->disable_flag_override();
-    app.add_flag("--val", val);
-
-    run();
-
-    EXPECT_EQ(2, two);
-    EXPECT_EQ(4, four);
-    EXPECT_EQ(15, val);
-}
-
-TEST_F(TApp, IniOutputSimple) {
-
-    int v;
-    app.add_option("--simple", v);
-
-    args = {"--simple=3"};
-
-    run();
-
-    std::string str = app.config_to_str();
-    EXPECT_EQ("simple=3\n", str);
-}
-
-TEST_F(TApp, IniOutputNoConfigurable) {
-
-    int v1, v2;
-    app.add_option("--simple", v1);
-    app.add_option("--noconf", v2)->configurable(false);
-
-    args = {"--simple=3", "--noconf=2"};
-
-    run();
-
-    std::string str = app.config_to_str();
-    EXPECT_EQ("simple=3\n", str);
-}
-
-TEST_F(TApp, IniOutputShortSingleDescription) {
-    std::string flag = "some_flag";
-    const std::string description = "Some short description.";
-    app.add_flag("--" + flag, description);
-
-    run();
-
-    std::string str = app.config_to_str(true, true);
-    EXPECT_THAT(str, HasSubstr("; " + description + "\n" + flag + "=false\n"));
-}
-
-TEST_F(TApp, IniOutputShortDoubleDescription) {
-    std::string flag1 = "flagnr1";
-    std::string flag2 = "flagnr2";
-    const std::string description1 = "First description.";
-    const std::string description2 = "Second description.";
-    app.add_flag("--" + flag1, description1);
-    app.add_flag("--" + flag2, description2);
-
-    run();
-
-    std::string str = app.config_to_str(true, true);
-    EXPECT_EQ(str, "; " + description1 + "\n" + flag1 + "=false\n\n; " + description2 + "\n" + flag2 + "=false\n");
-}
-
-TEST_F(TApp, IniOutputMultiLineDescription) {
-    std::string flag = "some_flag";
-    const std::string description = "Some short description.\nThat has lines.";
-    app.add_flag("--" + flag, description);
-
-    run();
-
-    std::string str = app.config_to_str(true, true);
-    EXPECT_THAT(str, HasSubstr("; Some short description.\n"));
-    EXPECT_THAT(str, HasSubstr("; That has lines.\n"));
-    EXPECT_THAT(str, HasSubstr(flag + "=false\n"));
-}
-
-TEST_F(TApp, IniOutputVector) {
-
-    std::vector<int> v;
-    app.add_option("--vector", v);
-
-    args = {"--vector", "1", "2", "3"};
-
-    run();
-
-    std::string str = app.config_to_str();
-    EXPECT_EQ("vector=1 2 3\n", str);
-}
-
-TEST_F(TApp, IniOutputFlag) {
-
-    int v, q;
-    app.add_option("--simple", v);
-    app.add_flag("--nothing");
-    app.add_flag("--onething");
-    app.add_flag("--something", q);
-
-    args = {"--simple=3", "--onething", "--something", "--something"};
-
-    run();
-
-    std::string str = app.config_to_str();
-    EXPECT_THAT(str, HasSubstr("simple=3"));
-    EXPECT_THAT(str, Not(HasSubstr("nothing")));
-    EXPECT_THAT(str, HasSubstr("onething=true"));
-    EXPECT_THAT(str, HasSubstr("something=2"));
-
-    str = app.config_to_str(true);
-    EXPECT_THAT(str, HasSubstr("nothing"));
-}
-
-TEST_F(TApp, IniOutputSet) {
-
-    int v;
-    app.add_option("--simple", v)->check(CLI::IsMember({1, 2, 3}));
-
-    args = {"--simple=2"};
-
-    run();
-
-    std::string str = app.config_to_str();
-    EXPECT_THAT(str, HasSubstr("simple=2"));
-}
-
-TEST_F(TApp, IniOutputDefault) {
-
-    int v = 7;
-    app.add_option("--simple", v, "", true);
-
-    run();
-
-    std::string str = app.config_to_str();
-    EXPECT_THAT(str, Not(HasSubstr("simple=7")));
-
-    str = app.config_to_str(true);
-    EXPECT_THAT(str, HasSubstr("simple=7"));
-}
-
-TEST_F(TApp, IniOutputSubcom) {
-
-    app.add_flag("--simple");
-    auto subcom = app.add_subcommand("other");
-    subcom->add_flag("--newer");
-
-    args = {"--simple", "other", "--newer"};
-    run();
-
-    std::string str = app.config_to_str();
-    EXPECT_THAT(str, HasSubstr("simple=true"));
-    EXPECT_THAT(str, HasSubstr("other.newer=true"));
-}
-
-TEST_F(TApp, IniQuotedOutput) {
-
-    std::string val1;
-    app.add_option("--val1", val1);
-
-    std::string val2;
-    app.add_option("--val2", val2);
-
-    args = {"--val1", "I am a string", "--val2", R"(I am a "confusing" string)"};
-
-    run();
-
-    EXPECT_EQ("I am a string", val1);
-    EXPECT_EQ("I am a \"confusing\" string", val2);
-
-    std::string str = app.config_to_str();
-    EXPECT_THAT(str, HasSubstr("val1=\"I am a string\""));
-    EXPECT_THAT(str, HasSubstr("val2='I am a \"confusing\" string'"));
-}
-
-TEST_F(TApp, DefaultsIniQuotedOutput) {
-
-    std::string val1{"I am a string"};
-    app.add_option("--val1", val1, "", true);
-
-    std::string val2{R"(I am a "confusing" string)"};
-    app.add_option("--val2", val2, "", true);
-
-    run();
-
-    std::string str = app.config_to_str(true);
-    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 4a3997af5f4da288fcd4d07874dbc499ba9f533f..6d7a4873307cba16054369e0c4a29d966b8f3b58 100644
--- a/packages/CLI11/tests/NewParseTest.cpp
+++ b/packages/CLI11/tests/NewParseTest.cpp
@@ -1,6 +1,7 @@
 #include "app_helper.hpp"
 #include "gmock/gmock.h"
 #include <complex>
+#include <cstdint>
 
 using ::testing::HasSubstr;
 
@@ -77,6 +78,26 @@ TEST_F(TApp, BuiltinComplex) {
     EXPECT_DOUBLE_EQ(3, comp.imag());
 }
 
+TEST_F(TApp, BuiltinComplexFloat) {
+    std::complex<float> comp{1, 2};
+    app.add_complex<std::complex<float>, float>("-c,--complex", comp, "", true);
+
+    args = {"-c", "4", "3"};
+
+    std::string help = app.help();
+    EXPECT_THAT(help, HasSubstr("1"));
+    EXPECT_THAT(help, HasSubstr("2"));
+    EXPECT_THAT(help, HasSubstr("COMPLEX"));
+
+    EXPECT_FLOAT_EQ(1, comp.real());
+    EXPECT_FLOAT_EQ(2, comp.imag());
+
+    run();
+
+    EXPECT_FLOAT_EQ(4, comp.real());
+    EXPECT_FLOAT_EQ(3, comp.imag());
+}
+
 TEST_F(TApp, BuiltinComplexWithDelimiter) {
     cx comp{1, 2};
     app.add_complex("-c,--complex", comp, "", true)->delimiter('+');
@@ -121,21 +142,70 @@ TEST_F(TApp, BuiltinComplexIgnoreI) {
     EXPECT_DOUBLE_EQ(3, comp.imag());
 }
 
-TEST_F(TApp, BuiltinComplexFail) {
+TEST_F(TApp, BuiltinComplexSingleArg) {
     cx comp{1, 2};
     app.add_complex("-c,--complex", comp);
 
     args = {"-c", "4"};
+    run();
+    EXPECT_DOUBLE_EQ(4, comp.real());
+    EXPECT_DOUBLE_EQ(0, comp.imag());
+
+    args = {"-c", "4-2i"};
+    run();
+    EXPECT_DOUBLE_EQ(4, comp.real());
+    EXPECT_DOUBLE_EQ(-2, comp.imag());
+    args = {"-c", "4+2i"};
+    run();
+    EXPECT_DOUBLE_EQ(4, comp.real());
+    EXPECT_DOUBLE_EQ(2, comp.imag());
+
+    args = {"-c", "-4+2j"};
+    run();
+    EXPECT_DOUBLE_EQ(-4, comp.real());
+    EXPECT_DOUBLE_EQ(2, comp.imag());
+
+    args = {"-c", "-4.2-2j"};
+    run();
+    EXPECT_DOUBLE_EQ(-4.2, comp.real());
+    EXPECT_DOUBLE_EQ(-2, comp.imag());
+
+    args = {"-c", "-4.2-2.7i"};
+    run();
+    EXPECT_DOUBLE_EQ(-4.2, comp.real());
+    EXPECT_DOUBLE_EQ(-2.7, comp.imag());
+}
+
+TEST_F(TApp, BuiltinComplexSingleImag) {
+    cx comp{1, 2};
+    app.add_complex("-c,--complex", comp);
 
-    EXPECT_THROW(run(), CLI::ArgumentMismatch);
+    args = {"-c", "4j"};
+    run();
+    EXPECT_DOUBLE_EQ(0, comp.real());
+    EXPECT_DOUBLE_EQ(4, comp.imag());
+
+    args = {"-c", "-4j"};
+    run();
+    EXPECT_DOUBLE_EQ(0, comp.real());
+    EXPECT_DOUBLE_EQ(-4, comp.imag());
+    args = {"-c", "-4"};
+    run();
+    EXPECT_DOUBLE_EQ(-4, comp.real());
+    EXPECT_DOUBLE_EQ(0, comp.imag());
+    args = {"-c", "+4"};
+    run();
+    EXPECT_DOUBLE_EQ(4, comp.real());
+    EXPECT_DOUBLE_EQ(0, comp.imag());
 }
 
+/// Simple class containing two strings useful for testing lexical cast and conversions
 class spair {
   public:
     spair() = default;
     spair(const std::string &s1, const std::string &s2) : first(s1), second(s2) {}
-    std::string first;
-    std::string second;
+    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
@@ -199,7 +269,7 @@ template <> bool lexical_cast<std::complex<double>>(const std::string &input, st
         R"(([+-]?(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?)\s*([+-]\s*(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?)[ji]*)");
 
     std::smatch m;
-    double x = 0.0, y = 0.0;
+    double x{0.0}, y{0.0};
     bool worked;
     std::regex_search(input, m, creg);
     if(m.size() == 9) {
@@ -283,7 +353,7 @@ template <class X> class objWrapper {
     const X &value() const { return val_; }
 
   private:
-    X val_;
+    X val_{};
 };
 
 // I think there is a bug with the is_assignable in visual studio 2015 it is fixed in later versions
@@ -417,22 +487,25 @@ template <class X> class AobjWrapper {
     // delete all other constructors
     template <class TT> AobjWrapper(TT &&obj) = delete;
     // single assignment operator
-    void operator=(X val) { val_ = val; }
+    AobjWrapper &operator=(X val) {
+        val_ = val;
+        return *this;
+    }
     // delete all other assignment operators
     template <typename TT> void operator=(TT &&obj) = delete;
 
     const X &value() const { return val_; }
 
   private:
-    X val_;
+    X val_{};
 };
 
-static_assert(std::is_assignable<AobjWrapper<uint16_t> &, uint16_t>::value,
+static_assert(std::is_assignable<AobjWrapper<std::uint16_t> &, std::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);
+    AobjWrapper<std::uint16_t> sWrapper;
+    app.add_option<AobjWrapper<std::uint16_t>, std::uint16_t>("-v", sWrapper);
     args = {"-v", "9"};
 
     run();
diff --git a/packages/CLI11/tests/OptionGroupTest.cpp b/packages/CLI11/tests/OptionGroupTest.cpp
index e109c3bf26675226a789e3d9faddc887c84446d2..91f954dfacf8fb77f0947b6d08e8487e93607cb7 100644
--- a/packages/CLI11/tests/OptionGroupTest.cpp
+++ b/packages/CLI11/tests/OptionGroupTest.cpp
@@ -23,11 +23,11 @@ TEST_F(TApp, BasicOptionGroup) {
 
 TEST_F(TApp, BasicOptionGroupExact) {
     auto ogroup = app.add_option_group("clusters");
-    int res;
+    int res{0};
     ogroup->add_option("--test1", res);
     ogroup->add_option("--test2", res);
     ogroup->add_option("--test3", res);
-    int val2;
+    int val2{0};
     app.add_option("--option", val2);
     ogroup->require_option(1);
     args = {"--test1", "5"};
@@ -47,11 +47,11 @@ TEST_F(TApp, BasicOptionGroupExact) {
 
 TEST_F(TApp, BasicOptionGroupExactTooMany) {
     auto ogroup = app.add_option_group("clusters");
-    int res;
+    int res{0};
     ogroup->add_option("--test1", res);
     ogroup->add_option("--test2", res);
     ogroup->add_option("--test3", res);
-    int val2;
+    int val2{0};
     app.add_option("--option", val2);
     ogroup->require_option(10);
     args = {"--test1", "5"};
@@ -60,11 +60,11 @@ TEST_F(TApp, BasicOptionGroupExactTooMany) {
 
 TEST_F(TApp, BasicOptionGroupMinMax) {
     auto ogroup = app.add_option_group("clusters");
-    int res;
+    int res{0};
     ogroup->add_option("--test1", res);
     ogroup->add_option("--test2", res);
     ogroup->add_option("--test3", res);
-    int val2;
+    int val2{0};
     app.add_option("--option", val2);
     ogroup->require_option(1, 1);
     args = {"--test1", "5"};
@@ -84,11 +84,11 @@ TEST_F(TApp, BasicOptionGroupMinMax) {
 
 TEST_F(TApp, BasicOptionGroupMinMaxDifferent) {
     auto ogroup = app.add_option_group("clusters");
-    int res;
+    int res{0};
     ogroup->add_option("--test1", res);
     ogroup->add_option("--test2", res);
     ogroup->add_option("--test3", res);
-    int val2;
+    int val2{0};
     app.add_option("--option", val2);
     ogroup->require_option(1, 2);
     args = {"--test1", "5"};
@@ -112,11 +112,11 @@ TEST_F(TApp, BasicOptionGroupMinMaxDifferent) {
 
 TEST_F(TApp, BasicOptionGroupMinMaxDifferentReversed) {
     auto ogroup = app.add_option_group("clusters");
-    int res;
+    int res{0};
     ogroup->add_option("--test1", res);
     ogroup->add_option("--test2", res);
     ogroup->add_option("--test3", res);
-    int val2;
+    int val2{0};
     app.add_option("--option", val2);
     ogroup->require_option(2, 1);
     EXPECT_EQ(ogroup->get_require_option_min(), 2u);
@@ -144,7 +144,7 @@ TEST_F(TApp, BasicOptionGroupMinMaxDifferentReversed) {
 
 TEST_F(TApp, BasicOptionGroupMax) {
     auto ogroup = app.add_option_group("clusters");
-    int res;
+    int res{0};
     ogroup->add_option("--test1", res);
     ogroup->add_option("--test2", res);
     ogroup->add_option("--test3", res);
@@ -168,11 +168,11 @@ TEST_F(TApp, BasicOptionGroupMax) {
 
 TEST_F(TApp, BasicOptionGroupMax1) {
     auto ogroup = app.add_option_group("clusters");
-    int res;
+    int res{0};
     ogroup->add_option("--test1", res);
     ogroup->add_option("--test2", res);
     ogroup->add_option("--test3", res);
-    int val2;
+    int val2{0};
     app.add_option("--option", val2);
     ogroup->require_option(-1);
     args = {"--test1", "5"};
@@ -192,11 +192,11 @@ TEST_F(TApp, BasicOptionGroupMax1) {
 
 TEST_F(TApp, BasicOptionGroupMin) {
     auto ogroup = app.add_option_group("clusters");
-    int res;
+    int res{0};
     ogroup->add_option("--test1", res);
     ogroup->add_option("--test2", res);
     ogroup->add_option("--test3", res);
-    int val2;
+    int val2{0};
     app.add_option("--option", val2);
     ogroup->require_option();
 
@@ -213,11 +213,11 @@ TEST_F(TApp, BasicOptionGroupMin) {
 
 TEST_F(TApp, BasicOptionGroupExact2) {
     auto ogroup = app.add_option_group("clusters");
-    int res;
+    int res{0};
     ogroup->add_option("--test1", res);
     ogroup->add_option("--test2", res);
     ogroup->add_option("--test3", res);
-    int val2;
+    int val2{0};
     app.add_option("--option", val2);
     ogroup->require_option(2);
 
@@ -237,11 +237,11 @@ TEST_F(TApp, BasicOptionGroupExact2) {
 
 TEST_F(TApp, BasicOptionGroupMin2) {
     auto ogroup = app.add_option_group("clusters");
-    int res;
+    int res{0};
     ogroup->add_option("--test1", res);
     ogroup->add_option("--test2", res);
     ogroup->add_option("--test3", res);
-    int val2;
+    int val2{0};
     app.add_option("--option", val2);
     ogroup->require_option(2, 0);
 
@@ -258,11 +258,11 @@ TEST_F(TApp, BasicOptionGroupMin2) {
 
 TEST_F(TApp, BasicOptionGroupMinMoved) {
 
-    int res;
+    int res{0};
     auto opt1 = app.add_option("--test1", res);
     auto opt2 = app.add_option("--test2", res);
     auto opt3 = app.add_option("--test3", res);
-    int val2;
+    int val2{0};
     app.add_option("--option", val2);
 
     auto ogroup = app.add_option_group("clusters");
@@ -287,11 +287,11 @@ TEST_F(TApp, BasicOptionGroupMinMoved) {
 
 TEST_F(TApp, BasicOptionGroupMinMovedAsGroup) {
 
-    int res;
+    int res{0};
     auto opt1 = app.add_option("--test1", res);
     auto opt2 = app.add_option("--test2", res);
     auto opt3 = app.add_option("--test3", res);
-    int val2;
+    int val2{0};
     app.add_option("--option", val2);
 
     auto ogroup = app.add_option_group("clusters");
@@ -315,10 +315,10 @@ TEST_F(TApp, BasicOptionGroupMinMovedAsGroup) {
 
 TEST_F(TApp, BasicOptionGroupAddFailures) {
 
-    int res;
+    int res{0};
     auto opt1 = app.add_option("--test1", res);
     app.set_config("--config");
-    int val2;
+    int val2{0};
     app.add_option("--option", val2);
 
     auto ogroup = app.add_option_group("clusters");
@@ -341,10 +341,10 @@ TEST_F(TApp, BasicOptionGroupAddFailures) {
 
 TEST_F(TApp, BasicOptionGroupScrewedUpMove) {
 
-    int res;
+    int res{0};
     auto opt1 = app.add_option("--test1", res);
     auto opt2 = app.add_option("--test2", res);
-    int val2;
+    int val2{0};
     app.add_option("--option", val2);
 
     auto ogroup = app.add_option_group("clusters");
@@ -370,16 +370,20 @@ TEST_F(TApp, InvalidOptions) {
 
 struct ManyGroups : public TApp {
 
-    CLI::Option_group *main;
-    CLI::Option_group *g1;
-    CLI::Option_group *g2;
-    CLI::Option_group *g3;
-    std::string name1;
-    std::string name2;
-    std::string name3;
-    std::string val1;
-    std::string val2;
-    std::string val3;
+    CLI::Option_group *main{nullptr};
+    CLI::Option_group *g1{nullptr};
+    CLI::Option_group *g2{nullptr};
+    CLI::Option_group *g3{nullptr};
+    std::string name1{};
+    std::string name2{};
+    std::string name3{};
+    std::string val1{};
+    std::string val2{};
+    std::string val3{};
+
+    ManyGroups(const ManyGroups &) = delete;
+    ManyGroups &operator=(const ManyGroups &) = delete;
+
     ManyGroups() {
         main = app.add_option_group("main", "the main outer group");
         g1 = main->add_option_group("g1", "group1 description");
@@ -671,14 +675,14 @@ TEST_F(ManyGroups, Moving) {
 }
 
 struct ManyGroupsPreTrigger : public ManyGroups {
-    size_t triggerMain{0u}, trigger1{87u}, trigger2{34u}, trigger3{27u};
+    std::size_t triggerMain{0u}, trigger1{87u}, trigger2{34u}, trigger3{27u};
     ManyGroupsPreTrigger() {
         remove_required();
-        app.preparse_callback([this](size_t count) { triggerMain = count; });
+        app.preparse_callback([this](std::size_t count) { triggerMain = count; });
 
-        g1->preparse_callback([this](size_t count) { trigger1 = count; });
-        g2->preparse_callback([this](size_t count) { trigger2 = count; });
-        g3->preparse_callback([this](size_t count) { trigger3 = count; });
+        g1->preparse_callback([this](std::size_t count) { trigger1 = count; });
+        g2->preparse_callback([this](std::size_t count) { trigger2 = count; });
+        g3->preparse_callback([this](std::size_t count) { trigger3 = count; });
     }
 };
 
@@ -743,8 +747,8 @@ TEST_F(ManyGroupsPreTrigger, PreTriggerTestsSubcommand) {
     g2->add_subcommand("sub2")->fallthrough();
     g3->add_subcommand("sub3")->fallthrough();
 
-    size_t subtrigger;
-    sub1->preparse_callback([&subtrigger](size_t count) { subtrigger = count; });
+    std::size_t subtrigger;
+    sub1->preparse_callback([&subtrigger](std::size_t count) { subtrigger = count; });
     args = {"sub1"};
     run();
     EXPECT_EQ(triggerMain, 1u);
diff --git a/packages/CLI11/tests/OptionalTest.cpp b/packages/CLI11/tests/OptionalTest.cpp
index 2710061d0d5b36d125a261a173477a9979e2ee25..598d16c55886e1da6527bff83ebdf3251460f380 100644
--- a/packages/CLI11/tests/OptionalTest.cpp
+++ b/packages/CLI11/tests/OptionalTest.cpp
@@ -1,3 +1,4 @@
+#include <cstdint>
 #include <cstdlib>
 #include <iostream>
 
@@ -47,6 +48,13 @@
 
 #if CLI11_STD_OPTIONAL
 
+#ifdef _MSC_VER
+// this warning suppresses double to int conversions that are inherent in the test
+// on windows.  This may be able to removed in the future as the add_option capability
+// improves
+#pragma warning(disable : 4244)
+#endif
+
 TEST_F(TApp, StdOptionalTest) {
     std::optional<int> opt;
     app.add_option("-c,--count", opt);
@@ -64,6 +72,10 @@ TEST_F(TApp, StdOptionalTest) {
     EXPECT_EQ(*opt, 3);
 }
 
+#ifdef _MSC_VER
+#pragma warning(default : 4244)
+#endif
+
 #endif
 #if CLI11_EXPERIMENTAL_OPTIONAL
 
@@ -104,8 +116,24 @@ TEST_F(TApp, BoostOptionalTest) {
     EXPECT_EQ(*opt, 3);
 }
 
+TEST_F(TApp, BoostOptionalTestZarg) {
+    boost::optional<int> opt;
+    app.add_option("-c,--count", opt)->expected(0, 1);
+    run();
+    EXPECT_FALSE(opt);
+
+    args = {"-c", "1"};
+    run();
+    EXPECT_TRUE(opt);
+    EXPECT_EQ(*opt, 1);
+    opt = {};
+    args = {"--count"};
+    run();
+    EXPECT_FALSE(opt);
+}
+
 TEST_F(TApp, BoostOptionalint64Test) {
-    boost::optional<int64_t> opt;
+    boost::optional<std::int64_t> opt;
     app.add_option("-c,--count", opt);
     run();
     EXPECT_FALSE(opt);
@@ -175,6 +203,22 @@ TEST_F(TApp, BoostOptionalVector) {
     EXPECT_EQ(*opt, expV);
 }
 
+TEST_F(TApp, BoostOptionalVectorEmpty) {
+    boost::optional<std::vector<int>> opt;
+    app.add_option<decltype(opt), std::vector<int>>("-v,--vec", opt)->expected(0, 3)->allow_extra_args();
+    run();
+    EXPECT_FALSE(opt);
+    args = {"-v"};
+    opt = std::vector<int>{4, 3};
+    run();
+    EXPECT_FALSE(opt);
+    args = {"-v", "1", "4", "5"};
+    run();
+    EXPECT_TRUE(opt);
+    std::vector<int> expV{1, 4, 5};
+    EXPECT_EQ(*opt, expV);
+}
+
 #endif
 
 #if !CLI11_OPTIONAL
diff --git a/packages/CLI11/tests/SetTest.cpp b/packages/CLI11/tests/SetTest.cpp
index 30d400b4bf80f0e668ae4bfbbff9b32e51b47432..e86bdc6ff6107ef565b605fb1dfd4c93074ce29e 100644
--- a/packages/CLI11/tests/SetTest.cpp
+++ b/packages/CLI11/tests/SetTest.cpp
@@ -26,7 +26,7 @@ static_assert(CLI::detail::pair_adaptor<std::map<int, int>>::value == true, "Sho
 static_assert(CLI::detail::pair_adaptor<std::vector<std::pair<int, int>>>::value == true, "Should have pairs");
 
 TEST_F(TApp, SimpleMaps) {
-    int value;
+    int value{0};
     std::map<std::string, int> map = {{"one", 1}, {"two", 2}};
     auto opt = app.add_option("-s,--set", value)->transform(CLI::Transformer(map));
     args = {"-s", "one"};
@@ -306,7 +306,7 @@ TEST_F(TApp, SimiShortcutSets) {
 }
 
 TEST_F(TApp, SetFromCharStarArrayVector) {
-    constexpr const char *names[] = {"one", "two", "three"};
+    constexpr const char *names[3]{"one", "two", "three"};
     std::string value;
     auto opt = app.add_option("-s,--set", value)
                    ->check(CLI::IsMember{std::vector<std::string>(std::begin(names), std::end(names))});
@@ -319,7 +319,7 @@ TEST_F(TApp, SetFromCharStarArrayVector) {
 }
 
 TEST_F(TApp, OtherTypeSets) {
-    int value;
+    int value{0};
     std::vector<int> set = {2, 3, 4};
     auto opt = app.add_option("--set", value)->check(CLI::IsMember(set));
     args = {"--set", "3"};
@@ -353,7 +353,7 @@ TEST_F(TApp, OtherTypeSets) {
 }
 
 TEST_F(TApp, NumericalSets) {
-    int value;
+    int value{0};
     auto opt = app.add_option("-s,--set", value)->check(CLI::IsMember{std::set<int>({1, 2, 3})});
     args = {"-s", "1"};
     run();
@@ -366,7 +366,7 @@ TEST_F(TApp, NumericalSets) {
 // Converted original set tests
 
 TEST_F(TApp, SetWithDefaults) {
-    int someint = 2;
+    int someint{2};
     app.add_option("-a", someint, "", true)->check(CLI::IsMember({1, 2, 3, 4}));
 
     args = {"-a1", "-a2"};
@@ -375,7 +375,7 @@ TEST_F(TApp, SetWithDefaults) {
 }
 
 TEST_F(TApp, SetWithDefaultsConversion) {
-    int someint = 2;
+    int someint{2};
     app.add_option("-a", someint, "", true)->check(CLI::IsMember({1, 2, 3, 4}));
 
     args = {"-a", "hi"};
@@ -442,7 +442,7 @@ TEST_F(TApp, InCaselessSetWithDefault) {
 
 TEST_F(TApp, InIntSet) {
 
-    int choice;
+    int choice{0};
     app.add_option("-q,--quick", choice)->check(CLI::IsMember({1, 2, 3}));
 
     args = {"--quick", "2"};
@@ -456,7 +456,7 @@ TEST_F(TApp, InIntSet) {
 
 TEST_F(TApp, InIntSetWindows) {
 
-    int choice;
+    int choice{0};
     app.add_option("-q,--quick", choice)->check(CLI::IsMember({1, 2, 3}));
     app.allow_windows_style_options();
     args = {"/q", "2"};
@@ -473,7 +473,7 @@ TEST_F(TApp, InIntSetWindows) {
 
 TEST_F(TApp, FailSet) {
 
-    int choice;
+    int choice{0};
     app.add_option("-q,--quick", choice)->check(CLI::IsMember({1, 2, 3}));
 
     args = {"--quick", "3", "--quick=2"};
@@ -485,7 +485,7 @@ TEST_F(TApp, FailSet) {
 
 TEST_F(TApp, FailMutableSet) {
 
-    int choice;
+    int choice{0};
     auto vals = std::shared_ptr<std::set<int>>(new std::set<int>({1, 2, 3}));
     app.add_option("-q,--quick", choice)->check(CLI::IsMember(vals));
     app.add_option("-s,--slow", choice, "", true)->check(CLI::IsMember(vals));
diff --git a/packages/CLI11/tests/SubcommandTest.cpp b/packages/CLI11/tests/SubcommandTest.cpp
index 336dd924bbcf1a11e39b3b0e933ec456d28e14e7..b9994a56ae825bbee6526d99564f0e2477129a34 100644
--- a/packages/CLI11/tests/SubcommandTest.cpp
+++ b/packages/CLI11/tests/SubcommandTest.cpp
@@ -192,7 +192,7 @@ TEST_F(TApp, DuplicateSubcommands) {
 TEST_F(TApp, DuplicateSubcommandCallbacks) {
 
     auto foo = app.add_subcommand("foo");
-    int count = 0;
+    int count{0};
     foo->callback([&count]() { ++count; });
     foo->immediate_callback();
     EXPECT_TRUE(foo->get_immediate_callback());
@@ -208,7 +208,7 @@ TEST_F(TApp, DuplicateSubcommandCallbacks) {
 TEST_F(TApp, DuplicateSubcommandCallbacksValues) {
 
     auto foo = app.add_subcommand("foo");
-    int val;
+    int val{0};
     foo->add_option("--val", val);
     std::vector<int> vals;
     foo->callback([&vals, &val]() { vals.push_back(val); });
@@ -231,7 +231,7 @@ TEST_F(TApp, Callbacks) {
     auto sub1 = app.add_subcommand("sub1");
     sub1->callback([]() { throw CLI::Success(); });
     auto sub2 = app.add_subcommand("sub2");
-    bool val = false;
+    bool val{false};
     sub2->callback([&val]() { val = true; });
 
     args = {"sub2"};
@@ -245,13 +245,15 @@ 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)); });
+    auto sub1 =
+        app.add_subcommand("sub1")
+            ->parse_complete_callback([&cb]() { cb.push_back("c1"); })
+            ->preparse_callback([&cb](std::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](std::size_t v1) { cb.push_back(std::string("pc2-") + std::to_string(v1)); });
+    app.preparse_callback([&cb](std::size_t v1) { cb.push_back(std::string("pa-") + std::to_string(v1)); });
 
     app.add_option("--opt1");
     sub1->add_flag("--sub1opt");
@@ -351,7 +353,7 @@ TEST_F(TApp, RuntimeErrorInCallback) {
 }
 
 TEST_F(TApp, NoFallThroughOpts) {
-    int val = 1;
+    int val{1};
     app.add_option("--val", val);
 
     app.add_subcommand("sub");
@@ -361,7 +363,7 @@ TEST_F(TApp, NoFallThroughOpts) {
 }
 
 TEST_F(TApp, NoFallThroughPositionals) {
-    int val = 1;
+    int val{1};
     app.add_option("val", val);
 
     app.add_subcommand("sub");
@@ -371,7 +373,7 @@ TEST_F(TApp, NoFallThroughPositionals) {
 }
 
 TEST_F(TApp, NoFallThroughOptsWithTerminator) {
-    int val = 1;
+    int val{1};
     app.add_option("--val", val);
 
     app.add_subcommand("sub");
@@ -382,7 +384,7 @@ TEST_F(TApp, NoFallThroughOptsWithTerminator) {
 }
 
 TEST_F(TApp, NoFallThroughPositionalsWithTerminator) {
-    int val = 1;
+    int val{1};
     app.add_option("val", val);
 
     app.add_subcommand("sub");
@@ -400,7 +402,7 @@ TEST_F(TApp, NoFallThroughPositionalsWithTerminator) {
 TEST_F(TApp, NamelessSubComPositionals) {
 
     auto sub = app.add_subcommand();
-    int val = 1;
+    int val{1};
     sub->add_option("val", val);
 
     args = {"2"};
@@ -513,7 +515,7 @@ TEST_F(TApp, Nameless4LayerDeepMulti) {
 
 TEST_F(TApp, FallThroughRegular) {
     app.fallthrough();
-    int val = 1;
+    int val{1};
     app.add_option("--val", val);
 
     app.add_subcommand("sub");
@@ -525,7 +527,7 @@ TEST_F(TApp, FallThroughRegular) {
 
 TEST_F(TApp, FallThroughShort) {
     app.fallthrough();
-    int val = 1;
+    int val{1};
     app.add_option("-v", val);
 
     app.add_subcommand("sub");
@@ -537,7 +539,7 @@ TEST_F(TApp, FallThroughShort) {
 
 TEST_F(TApp, FallThroughPositional) {
     app.fallthrough();
-    int val = 1;
+    int val{1};
     app.add_option("val", val);
 
     app.add_subcommand("sub");
@@ -549,7 +551,7 @@ TEST_F(TApp, FallThroughPositional) {
 
 TEST_F(TApp, FallThroughEquals) {
     app.fallthrough();
-    int val = 1;
+    int val{1};
     app.add_option("--val", val);
 
     app.add_subcommand("sub");
@@ -561,7 +563,7 @@ TEST_F(TApp, FallThroughEquals) {
 
 TEST_F(TApp, EvilParseFallthrough) {
     app.fallthrough();
-    int val1 = 0, val2 = 0;
+    int val1{0}, val2{0};
     app.add_option("--val1", val1);
 
     auto sub = app.add_subcommand("sub");
@@ -577,7 +579,7 @@ TEST_F(TApp, EvilParseFallthrough) {
 
 TEST_F(TApp, CallbackOrdering) {
     app.fallthrough();
-    int val = 1, sub_val = 0;
+    int val{1}, sub_val{0};
     app.add_option("--val", val);
 
     auto sub = app.add_subcommand("sub");
@@ -596,7 +598,7 @@ TEST_F(TApp, CallbackOrdering) {
 
 TEST_F(TApp, CallbackOrderingImmediate) {
     app.fallthrough();
-    int val = 1, sub_val = 0;
+    int val{1}, sub_val{0};
     app.add_option("--val", val);
 
     auto sub = app.add_subcommand("sub")->immediate_callback();
@@ -615,7 +617,7 @@ TEST_F(TApp, CallbackOrderingImmediate) {
 
 TEST_F(TApp, CallbackOrderingImmediateMain) {
     app.fallthrough();
-    int val = 0, sub_val = 0;
+    int val{0}, sub_val{0};
 
     auto sub = app.add_subcommand("sub");
     sub->callback([&val, &sub_val]() {
@@ -809,12 +811,15 @@ TEST_F(TApp, RequiredPosInSubcommand) {
 
 struct SubcommandProgram : public TApp {
 
-    CLI::App *start;
-    CLI::App *stop;
+    CLI::App *start{nullptr};
+    CLI::App *stop{nullptr};
 
-    int dummy;
-    std::string file;
-    int count;
+    int dummy{0};
+    std::string file{};
+    int count{0};
+
+    SubcommandProgram(const SubcommandProgram &) = delete;
+    SubcommandProgram &operator=(const SubcommandProgram &) = delete;
 
     SubcommandProgram() {
         app.set_help_all_flag("--help-all");
@@ -1092,10 +1097,10 @@ TEST_F(SubcommandProgram, CallbackOrderImmediate) {
 
 struct ManySubcommands : public TApp {
 
-    CLI::App *sub1;
-    CLI::App *sub2;
-    CLI::App *sub3;
-    CLI::App *sub4;
+    CLI::App *sub1{nullptr};
+    CLI::App *sub2{nullptr};
+    CLI::App *sub3{nullptr};
+    CLI::App *sub4{nullptr};
 
     ManySubcommands() {
         app.allow_extras();
@@ -1105,6 +1110,9 @@ struct ManySubcommands : public TApp {
         sub4 = app.add_subcommand("sub4");
         args = {"sub1", "sub2", "sub3"};
     }
+
+    ManySubcommands(const ManySubcommands &) = delete;
+    ManySubcommands &operator=(const ManySubcommands &) = delete;
 };
 
 TEST_F(ManySubcommands, Required1Exact) {
@@ -1352,7 +1360,7 @@ TEST_F(ManySubcommands, SubcommandNeedsOptions) {
 }
 
 TEST_F(ManySubcommands, SubcommandNeedsOptionsCallbackOrdering) {
-    int count = 0;
+    int count{0};
     auto opt = app.add_flag("--subactive");
     app.add_flag("--flag1");
     sub1->needs(opt);
@@ -1453,7 +1461,7 @@ TEST_F(ManySubcommands, SubcommandTriggeredOn) {
 }
 
 TEST_F(TApp, UnnamedSub) {
-    double val;
+    double val{0.0};
     auto sub = app.add_subcommand("", "empty name");
     auto opt = sub->add_option("-v,--value", val);
     args = {"-v", "4.56"};
@@ -1473,7 +1481,7 @@ TEST_F(TApp, UnnamedSub) {
 }
 
 TEST_F(TApp, UnnamedSubMix) {
-    double val, val2, val3;
+    double val{0.0}, val2{0.0}, val3{0.0};
     app.add_option("-t", val2);
     auto sub1 = app.add_subcommand("", "empty name");
     sub1->add_option("-v,--value", val);
@@ -1489,7 +1497,7 @@ TEST_F(TApp, UnnamedSubMix) {
 }
 
 TEST_F(TApp, UnnamedSubMixExtras) {
-    double val, val2;
+    double val{0.0}, val2{0.0};
     app.add_option("-t", val2);
     auto sub = app.add_subcommand("", "empty name");
     sub->add_option("-v,--value", val);
@@ -1503,7 +1511,7 @@ TEST_F(TApp, UnnamedSubMixExtras) {
 }
 
 TEST_F(TApp, UnnamedSubNoExtras) {
-    double val, val2;
+    double val{0.0}, val2{0.0};
     app.add_option("-t", val2);
     auto sub = app.add_subcommand();
     sub->add_option("-v,--value", val);
@@ -1516,7 +1524,7 @@ TEST_F(TApp, UnnamedSubNoExtras) {
 }
 
 TEST_F(TApp, SubcommandAlias) {
-    double val;
+    double val{0.0};
     auto sub = app.add_subcommand("sub1");
     sub->alias("sub2");
     sub->alias("sub3");
@@ -1544,7 +1552,7 @@ TEST_F(TApp, SubcommandAlias) {
 }
 
 TEST_F(TApp, SubcommandAliasIgnoreCaseUnderscore) {
-    double val;
+    double val{0.0};
     auto sub = app.add_subcommand("sub1");
     sub->alias("sub2");
     sub->alias("sub3");
@@ -1587,7 +1595,7 @@ TEST_F(TApp, SubcommandAliasIgnoreCaseUnderscore) {
 }
 
 TEST_F(TApp, OptionGroupAlias) {
-    double val;
+    double val{0.0};
     auto sub = app.add_option_group("sub1");
     sub->alias("sub2");
     sub->alias("sub3");
@@ -1686,7 +1694,7 @@ TEST_F(TApp, AliasErrorsInOptionGroup) {
 }
 
 TEST(SharedSubTests, SharedSubcommand) {
-    double val, val2, val3, val4;
+    double val{0.0}, val2{0.0}, val3{0.0}, val4{0.0};
     CLI::App app1{"test program1"};
 
     app1.add_option("-t", val2);
@@ -1716,7 +1724,7 @@ TEST(SharedSubTests, SharedSubcommand) {
 }
 
 TEST(SharedSubTests, SharedSubIndependent) {
-    double val, val2, val4;
+    double val{0.0}, val2{0.0}, val4{0.0};
     CLI::App_p app1 = std::make_shared<CLI::App>("test program1");
     app1->allow_extras();
     app1->add_option("-t", val2);
@@ -1744,7 +1752,7 @@ TEST(SharedSubTests, SharedSubIndependent) {
 }
 
 TEST(SharedSubTests, SharedSubIndependentReuse) {
-    double val, val2, val4;
+    double val{0.0}, val2{0.0}, val4{0.0};
     CLI::App_p app1 = std::make_shared<CLI::App>("test program1");
     app1->allow_extras();
     app1->add_option("-t", val2);
diff --git a/packages/CLI11/tests/TransformTest.cpp b/packages/CLI11/tests/TransformTest.cpp
index ad3ce1382acf1978b1b50356509344b47d4c54b9..33bd9ce39d82e6bf0878982d24a024b2e57ee726 100644
--- a/packages/CLI11/tests/TransformTest.cpp
+++ b/packages/CLI11/tests/TransformTest.cpp
@@ -1,5 +1,7 @@
 #include "app_helper.hpp"
 
+#include <array>
+#include <cstdint>
 #include <unordered_map>
 
 #if defined(CLI11_CPP17)
@@ -12,7 +14,7 @@
 #endif
 
 TEST_F(TApp, SimpleTransform) {
-    int value;
+    int value{0};
     auto opt = app.add_option("-s", value)->transform(CLI::Transformer({{"one", std::string("1")}}));
     args = {"-s", "one"};
     run();
@@ -22,7 +24,7 @@ TEST_F(TApp, SimpleTransform) {
 }
 
 TEST_F(TApp, SimpleTransformInitList) {
-    int value;
+    int value{0};
     auto opt = app.add_option("-s", value)->transform(CLI::Transformer({{"one", "1"}}));
     args = {"-s", "one"};
     run();
@@ -32,7 +34,7 @@ TEST_F(TApp, SimpleTransformInitList) {
 }
 
 TEST_F(TApp, SimpleNumericalTransform) {
-    int value;
+    int value{0};
     auto opt = app.add_option("-s", value)->transform(CLI::Transformer(CLI::TransformPairs<int>{{"one", 1}}));
     args = {"-s", "one"};
     run();
@@ -42,8 +44,8 @@ TEST_F(TApp, SimpleNumericalTransform) {
 }
 
 TEST_F(TApp, EnumTransform) {
-    enum class test : int16_t { val1 = 3, val2 = 4, val3 = 17 };
-    test value;
+    enum class test : std::int16_t { val1 = 3, val2 = 4, val3 = 17 };
+    test value{test::val2};
     auto opt = app.add_option("-s", value)
                    ->transform(CLI::Transformer(
                        CLI::TransformPairs<test>{{"val1", test::val1}, {"val2", test::val2}, {"val3", test::val3}}));
@@ -67,12 +69,12 @@ TEST_F(TApp, EnumTransform) {
     // transformer doesn't do any checking so this still works
     args = {"-s", "5"};
     run();
-    EXPECT_EQ(static_cast<int16_t>(value), int16_t(5));
+    EXPECT_EQ(static_cast<std::int16_t>(value), std::int16_t(5));
 }
 
 TEST_F(TApp, EnumCheckedTransform) {
-    enum class test : int16_t { val1 = 3, val2 = 4, val3 = 17 };
-    test value;
+    enum class test : std::int16_t { val1 = 3, val2 = 4, val3 = 17 };
+    test value{test::val1};
     auto opt = app.add_option("-s", value)
                    ->transform(CLI::CheckedTransformer(
                        CLI::TransformPairs<test>{{"val1", test::val1}, {"val2", test::val2}, {"val3", test::val3}}));
@@ -101,8 +103,45 @@ TEST_F(TApp, EnumCheckedTransform) {
     EXPECT_THROW(run(), CLI::ValidationError);
 }
 
+// from jzakrzewski Issue #330
+TEST_F(TApp, EnumCheckedDefualtTransform) {
+    enum class existing : std::int16_t { abort, overwrite, remove };
+    app.add_option("--existing", "What to do if file already exists in the destination")
+        ->transform(
+            CLI::CheckedTransformer(std::unordered_map<std::string, existing>{{"abort", existing::abort},
+                                                                              {"overwrite", existing ::overwrite},
+                                                                              {"delete", existing::remove},
+                                                                              {"remove", existing::remove}}))
+        ->default_val("abort");
+    args = {"--existing", "overwrite"};
+    run();
+    EXPECT_EQ(app.get_option("--existing")->as<existing>(), existing::overwrite);
+    args.clear();
+    run();
+    EXPECT_EQ(app.get_option("--existing")->as<existing>(), existing::abort);
+}
+
+// test from https://github.com/CLIUtils/CLI11/issues/369  [Jakub Zakrzewski](https://github.com/jzakrzewski)
+TEST_F(TApp, EnumCheckedDefaultTransformCallback) {
+    enum class existing : std::int16_t { abort, overwrite, remove };
+    auto cmd = std::make_shared<CLI::App>("deploys the repository somewhere", "deploy");
+    cmd->add_option("--existing", "What to do if file already exists in the destination")
+        ->transform(
+            CLI::CheckedTransformer(std::unordered_map<std::string, existing>{{"abort", existing::abort},
+                                                                              {"overwrite", existing::overwrite},
+                                                                              {"delete", existing::remove},
+                                                                              {"remove", existing::remove}}))
+        ->default_val("abort");
+
+    cmd->callback([cmd]() { EXPECT_EQ(cmd->get_option("--existing")->as<existing>(), existing::abort); });
+    app.add_subcommand(cmd);
+
+    args = {"deploy"};
+    run();
+}
+
 TEST_F(TApp, SimpleTransformFn) {
-    int value;
+    int value{0};
     auto opt = app.add_option("-s", value)->transform(CLI::Transformer({{"one", "1"}}, CLI::ignore_case));
     args = {"-s", "ONE"};
     run();
@@ -125,7 +164,7 @@ TEST_F(TApp, StringViewTransformFn) {
 #endif
 
 TEST_F(TApp, SimpleNumericalTransformFn) {
-    int value;
+    int value{0};
     auto opt =
         app.add_option("-s", value)
             ->transform(CLI::Transformer(std::vector<std::pair<std::string, int>>{{"one", 1}}, CLI::ignore_case));
@@ -136,9 +175,57 @@ TEST_F(TApp, SimpleNumericalTransformFn) {
     EXPECT_EQ(value, 1);
 }
 
+TEST_F(TApp, SimpleNumericalTransformFnVector) {
+    std::vector<std::pair<std::string, int>> conversions{{"one", 1}, {"two", 2}};
+    int value{0};
+    auto opt = app.add_option("-s", value)->transform(CLI::Transformer(conversions, CLI::ignore_case));
+    args = {"-s", "ONe"};
+    run();
+    EXPECT_EQ(1u, app.count("-s"));
+    EXPECT_EQ(1u, opt->count());
+    EXPECT_EQ(value, 1);
+}
+
+TEST_F(TApp, SimpleNumericalTransformFnArray) {
+    std::array<std::pair<std::string, int>, 2> conversions;
+    conversions[0] = std::make_pair(std::string("one"), 1);
+    conversions[1] = std::make_pair(std::string("two"), 2);
+
+    int value{0};
+    auto opt = app.add_option("-s", value)->transform(CLI::Transformer(conversions, CLI::ignore_case));
+    args = {"-s", "ONe"};
+    run();
+    EXPECT_EQ(1u, app.count("-s"));
+    EXPECT_EQ(1u, opt->count());
+    EXPECT_EQ(value, 1);
+}
+
+#ifdef CLI11_CPP14
+// zero copy constexpr array operation with transformer example and test
+TEST_F(TApp, SimpleNumericalTransformFnconstexprArray) {
+    constexpr std::pair<const char *, int> p1{"one", 1};
+    constexpr std::pair<const char *, int> p2{"two", 2};
+    constexpr std::array<std::pair<const char *, int>, 2> conversions_c{{p1, p2}};
+
+    int value{0};
+    auto opt = app.add_option("-s", value)->transform(CLI::Transformer(&conversions_c, CLI::ignore_case));
+    args = {"-s", "ONe"};
+    run();
+    EXPECT_EQ(1u, app.count("-s"));
+    EXPECT_EQ(1u, opt->count());
+    EXPECT_EQ(value, 1);
+
+    args = {"-s", "twO"};
+    run();
+    EXPECT_EQ(1u, app.count("-s"));
+    EXPECT_EQ(1u, opt->count());
+    EXPECT_EQ(value, 2);
+}
+#endif
+
 TEST_F(TApp, EnumTransformFn) {
-    enum class test : int16_t { val1 = 3, val2 = 4, val3 = 17 };
-    test value;
+    enum class test : std::int16_t { val1 = 3, val2 = 4, val3 = 17 };
+    test value{test::val2};
     auto opt = app.add_option("-s", value)
                    ->transform(CLI::Transformer(
                        CLI::TransformPairs<test>{{"val1", test::val1}, {"val2", test::val2}, {"val3", test::val3}},
@@ -163,9 +250,9 @@ TEST_F(TApp, EnumTransformFn) {
 }
 
 TEST_F(TApp, EnumTransformFnMap) {
-    enum class test : int16_t { val1 = 3, val2 = 4, val3 = 17 };
+    enum class test : std::int16_t { val1 = 3, val2 = 4, val3 = 17 };
     std::map<std::string, test> map{{"val1", test::val1}, {"val2", test::val2}, {"val3", test::val3}};
-    test value;
+    test value{test::val3};
     auto opt = app.add_option("-s", value)->transform(CLI::Transformer(map, CLI::ignore_case, CLI::ignore_underscore));
     args = {"-s", "val_1"};
     run();
@@ -186,9 +273,9 @@ TEST_F(TApp, EnumTransformFnMap) {
 }
 
 TEST_F(TApp, EnumTransformFnPtrMap) {
-    enum class test : int16_t { val1 = 3, val2 = 4, val3 = 17, val4 = 37 };
+    enum class test : std::int16_t { val1 = 3, val2 = 4, val3 = 17, val4 = 37 };
     std::map<std::string, test> map{{"val1", test::val1}, {"val2", test::val2}, {"val3", test::val3}};
-    test value;
+    test value{test::val2};
     auto opt = app.add_option("-s", value)->transform(CLI::Transformer(&map, CLI::ignore_case, CLI::ignore_underscore));
     args = {"-s", "val_1"};
     run();
@@ -213,14 +300,14 @@ TEST_F(TApp, EnumTransformFnPtrMap) {
 }
 
 TEST_F(TApp, EnumTransformFnSharedPtrMap) {
-    enum class test : int16_t { val1 = 3, val2 = 4, val3 = 17, val4 = 37 };
+    enum class test : std::int16_t { val1 = 3, val2 = 4, val3 = 17, val4 = 37 };
     auto map = std::make_shared<std::unordered_map<std::string, test>>();
     auto &mp = *map;
     mp["val1"] = test::val1;
     mp["val2"] = test::val2;
     mp["val3"] = test::val3;
 
-    test value;
+    test value{test::val2};
     auto opt = app.add_option("-s", value)->transform(CLI::Transformer(map, CLI::ignore_case, CLI::ignore_underscore));
     args = {"-s", "val_1"};
     run();
@@ -478,7 +565,7 @@ TEST_F(TApp, NumberWithUnitCorrecltySplitNumber) {
 
 TEST_F(TApp, NumberWithUnitFloatTest) {
     std::map<std::string, double> mapping{{"a", 10}, {"b", 100}, {"cc", 1000}};
-    double value = 0;
+    double value{0.0};
     app.add_option("-n", value)->transform(CLI::AsNumberWithUnit(mapping));
 
     args = {"-n", "42"};
@@ -501,7 +588,7 @@ TEST_F(TApp, NumberWithUnitFloatTest) {
 TEST_F(TApp, NumberWithUnitCaseSensitive) {
     std::map<std::string, int> mapping{{"a", 10}, {"A", 100}};
 
-    int value = 0;
+    int value{0};
     app.add_option("-n", value)->transform(CLI::AsNumberWithUnit(mapping, CLI::AsNumberWithUnit::CASE_SENSITIVE));
 
     args = {"-n", "42a"};
@@ -516,7 +603,7 @@ TEST_F(TApp, NumberWithUnitCaseSensitive) {
 TEST_F(TApp, NumberWithUnitCaseInsensitive) {
     std::map<std::string, int> mapping{{"a", 10}, {"B", 100}};
 
-    int value = 0;
+    int value{0};
     app.add_option("-n", value)->transform(CLI::AsNumberWithUnit(mapping, CLI::AsNumberWithUnit::CASE_INSENSITIVE));
 
     args = {"-n", "42a"};
@@ -539,7 +626,7 @@ TEST_F(TApp, NumberWithUnitCaseInsensitive) {
 TEST_F(TApp, NumberWithUnitMandatoryUnit) {
     std::map<std::string, int> mapping{{"a", 10}, {"A", 100}};
 
-    int value;
+    int value{0};
     app.add_option("-n", value)
         ->transform(CLI::AsNumberWithUnit(mapping,
                                           CLI::AsNumberWithUnit::Options(CLI::AsNumberWithUnit::UNIT_REQUIRED |
@@ -560,7 +647,7 @@ TEST_F(TApp, NumberWithUnitMandatoryUnit) {
 TEST_F(TApp, NumberWithUnitMandatoryUnit2) {
     std::map<std::string, int> mapping{{"a", 10}, {"B", 100}};
 
-    int value;
+    int value{0};
     app.add_option("-n", value)
         ->transform(CLI::AsNumberWithUnit(mapping,
                                           CLI::AsNumberWithUnit::Options(CLI::AsNumberWithUnit::UNIT_REQUIRED |
@@ -590,7 +677,7 @@ TEST_F(TApp, NumberWithUnitBadMapping) {
 TEST_F(TApp, NumberWithUnitBadInput) {
     std::map<std::string, int> mapping{{"a", 10}, {"b", 100}};
 
-    int value;
+    int value{0};
     app.add_option("-n", value)->transform(CLI::AsNumberWithUnit(mapping));
 
     args = {"-n", "13 a b"};
@@ -612,7 +699,7 @@ TEST_F(TApp, NumberWithUnitBadInput) {
 TEST_F(TApp, NumberWithUnitIntOverflow) {
     std::map<std::string, int> mapping{{"a", 1000000}, {"b", 100}, {"c", 101}};
 
-    int32_t value;
+    std::int32_t value;
     app.add_option("-n", value)->transform(CLI::AsNumberWithUnit(mapping));
 
     args = {"-n", "1000 a"};
@@ -636,7 +723,7 @@ TEST_F(TApp, NumberWithUnitIntOverflow) {
 TEST_F(TApp, NumberWithUnitFloatOverflow) {
     std::map<std::string, float> mapping{{"a", 2.f}, {"b", 1.f}, {"c", 0.f}};
 
-    float value;
+    float value{0.0F};
     app.add_option("-n", value)->transform(CLI::AsNumberWithUnit(mapping));
 
     args = {"-n", "3e+38 a"};
@@ -652,7 +739,7 @@ TEST_F(TApp, NumberWithUnitFloatOverflow) {
 }
 
 TEST_F(TApp, AsSizeValue1000_1024) {
-    uint64_t value;
+    std::uint64_t value{0};
     app.add_option("-s", value)->transform(CLI::AsSizeValue(true));
 
     args = {"-s", "10240"};
@@ -663,8 +750,8 @@ TEST_F(TApp, AsSizeValue1000_1024) {
     run();
     EXPECT_EQ(value, 1u);
 
-    uint64_t k_value = 1000u;
-    uint64_t ki_value = 1024u;
+    std::uint64_t k_value{1000u};
+    std::uint64_t ki_value{1024u};
     args = {"-s", "1k"};
     run();
     EXPECT_EQ(value, k_value);
@@ -758,7 +845,7 @@ TEST_F(TApp, AsSizeValue1000_1024) {
 }
 
 TEST_F(TApp, AsSizeValue1024) {
-    uint64_t value;
+    std::uint64_t value{0};
     app.add_option("-s", value)->transform(CLI::AsSizeValue(false));
 
     args = {"-s", "10240"};
@@ -769,7 +856,7 @@ TEST_F(TApp, AsSizeValue1024) {
     run();
     EXPECT_EQ(value, 1u);
 
-    uint64_t ki_value = 1024u;
+    std::uint64_t ki_value{1024u};
     args = {"-s", "1k"};
     run();
     EXPECT_EQ(value, ki_value);
diff --git a/packages/CLI11/tests/TrueFalseTest.cpp b/packages/CLI11/tests/TrueFalseTest.cpp
index de634094bf129c377cec72f45a26c29b52ad86e4..546c3d24a5a5d484935c6383b995608a9461de9e 100644
--- a/packages/CLI11/tests/TrueFalseTest.cpp
+++ b/packages/CLI11/tests/TrueFalseTest.cpp
@@ -4,7 +4,7 @@
 struct TApp_TBO : public TApp, public ::testing::WithParamInterface<const char *> {};
 
 TEST_P(TApp_TBO, TrueBoolOption) {
-    bool value = false; // Not used, but set just in case
+    bool value{false}; // Not used, but set just in case
     app.add_option("-b,--bool", value);
     args = {"--bool", GetParam()};
     run();
@@ -19,7 +19,7 @@ INSTANTIATE_TEST_CASE_P(TrueBoolOptions, TApp_TBO, ::testing::Values("true", "on
 struct TApp_FBO : public TApp, public ::testing::WithParamInterface<const char *> {};
 
 TEST_P(TApp_FBO, FalseBoolOptions) {
-    bool value = true; // Not used, but set just in case
+    bool value{true}; // Not used, but set just in case
     app.add_option("-b,--bool", value);
     args = {"--bool", GetParam()};
     run();
diff --git a/packages/CLI11/tests/app_helper.hpp b/packages/CLI11/tests/app_helper.hpp
index 25f6224d822b7d78fc3c3763271b5d7cd086fbe4..eb8eb2b7d568772db1a5185edec55b616b0ca7e2 100644
--- a/packages/CLI11/tests/app_helper.hpp
+++ b/packages/CLI11/tests/app_helper.hpp
@@ -9,11 +9,11 @@
 #include "gtest/gtest.h"
 #include <iostream>
 
-typedef std::vector<std::string> input_t;
+using input_t = std::vector<std::string>;
 
 struct TApp : public ::testing::Test {
     CLI::App app{"My Test Program"};
-    input_t args;
+    input_t args{};
 
     void run() {
         // It is okay to re-parse - clear is called automatically before a parse.
@@ -24,10 +24,10 @@ struct TApp : public ::testing::Test {
 };
 
 class TempFile {
-    std::string _name;
+    std::string _name{};
 
   public:
-    explicit TempFile(std::string name) : _name(name) {
+    explicit TempFile(std::string name) : _name(std::move(name)) {
         if(!CLI::NonexistentPath(_name).empty())
             throw std::runtime_error(_name);
     }