~voidraven/scry

976b38f1f4bab63447e2cba5023ee57942a962ff — lotus 11 months ago
initial commit
A  => .gitignore +42 -0
@@ 1,42 @@
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
*.pyc
*.elc

# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
*.zstd
*.lz4

# Logs and databases #
######################
*.log
*.sql
*.sqlite

# OS generated files #
######################
.DS_Store
.DS_Store?
._*
*~
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
\ No newline at end of file

A  => .travis.yml +10 -0
@@ 1,10 @@
language: cpp
dist: bionic
compiler:
  - gcc
os:
  - linux
before_install:
  - sudo apt-get install -y libscrypt-dev
script:
  - make && make test && ./test_scry

A  => LICENSE.md +5 -0
@@ 1,5 @@
Copyright 2019 lotus

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

A  => README.md +50 -0
@@ 1,50 @@
![](https://url.png)

[![License](https://is.gd/GbWFMI)](https://opensource.org/licenses/ISC)

This tool provides a quick way do generate a [Scrypt](https://en.wikipedia.org/wiki/Scrypt) key from the shell. The normal scrypt cli tool provided [here](https://www.tarsnap.com/scrypt.html) will encrypt and decrypt files for you interactively. But, if you simply want it to give you a key, it cannot. 

### Dependencies
+ [libscrypt-dev](https://github.com/technion/libscrypt)
+ C++14 capable compiler
+ POSIX [make](https://is.gd/RB94Y8)

### Installation

Simply run:
```bash
git clone https://git.zerohack.xyz:443/lotus/scry.git
cd scry
make && sudo make install

# if you want to build a debug version
make debug

# if you want to build an optimized version
make opt

# to build and run tests
make test && ./test_scry
```

### Usage

```bash
# echo in your input
$ echo "SuperSecretPassword" | scry -s mysalt

# enter interactively
$ scry -s mysalt
SuperSecretPassword [enter]

Flags:
-s      # provided salt (a unique string aka nonce)
-h      # shows this help info
-v      # shows the version info
```

### Notes
This tool only reads input from stdin in order to avoid exposing sensitive information in the process list as arguments.

### License / Disclaimer
This project is licensed under the ISC license. (See LICENSE.md)

A  => clang_complete +1 -0
@@ 1,1 @@
-I/usr/include/c++/8/
\ No newline at end of file

A  => compile_commands.json +21 -0
@@ 1,21 @@
[
    {
        "arguments": [
            "c++", 
            "-c", 
            "--std=c++14", 
            "-pedantic", 
            "-Wall", 
            "-Wextra", 
            "-I", 
            "include", 
            "-I", 
            "tests", 
            "-I", 
            "/usr/local/include", 
            "main.cpp"
        ], 
        "directory": "/home/neb/git/scry", 
        "file": "main.cpp"
    }
]
\ No newline at end of file

A  => include/CLI11.hpp +6778 -0
@@ 1,6778 @@
#pragma once

// CLI11: Version 1.8.0
// Originally designed by Henry Schreiner
// https://github.com/CLIUtils/CLI11
//
// This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts
// from: v1.8.0
//
// From LICENSE:
//
// CLI11 1.8 Copyright (c) 2017-2019 University of Cincinnati, developed by Henry
// Schreiner under NSF AWARD 1414736. All rights reserved.
// 
// Redistribution and use in source and binary forms of CLI11, with or without
// modification, are permitted provided that the following conditions are met:
// 
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holder nor the names of its contributors
//    may be used to endorse or promote products derived from this software without
//    specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


// Standard combined includes:

#include <algorithm>
#include <cmath>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <istream>
#include <iterator>
#include <locale>
#include <map>
#include <memory>
#include <numeric>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>


// Verbatim copy from CLI/Version.hpp:


#define CLI11_VERSION_MAJOR 1
#define CLI11_VERSION_MINOR 8
#define CLI11_VERSION_PATCH 0
#define CLI11_VERSION "1.8.0"




// Verbatim copy from CLI/Macros.hpp:


// The following version macro is very similar to the one in PyBind11
#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER)
#if __cplusplus >= 201402L
#define CLI11_CPP14
#if __cplusplus >= 201703L
#define CLI11_CPP17
#if __cplusplus > 201703L
#define CLI11_CPP20
#endif
#endif
#endif
#elif defined(_MSC_VER) && __cplusplus == 199711L
// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented)
// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer
#if _MSVC_LANG >= 201402L
#define CLI11_CPP14
#if _MSVC_LANG > 201402L && _MSC_VER >= 1910
#define CLI11_CPP17
#if __MSVC_LANG > 201703L && _MSC_VER >= 1910
#define CLI11_CPP20
#endif
#endif
#endif
#endif

#if defined(CLI11_CPP14)
#define CLI11_DEPRECATED(reason) [[deprecated(reason)]]
#elif defined(_MSC_VER)
#define CLI11_DEPRECATED(reason) __declspec(deprecated(reason))
#else
#define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason)))
#endif




// Verbatim copy from CLI/Optional.hpp:


// You can explicitly enable or disable support
// by defining to 1 or 0. Extra check here to ensure it's in the stdlib too.
// We nest the check for __has_include and it's usage
#ifndef CLI11_STD_OPTIONAL
#ifdef __has_include
#if defined(CLI11_CPP17) && __has_include(<optional>)
#define CLI11_STD_OPTIONAL 1
#else
#define CLI11_STD_OPTIONAL 0
#endif
#else
#define CLI11_STD_OPTIONAL 0
#endif
#endif

#ifndef CLI11_EXPERIMENTAL_OPTIONAL
#define CLI11_EXPERIMENTAL_OPTIONAL 0
#endif

#ifndef CLI11_BOOST_OPTIONAL
#define CLI11_BOOST_OPTIONAL 0
#endif

#if CLI11_BOOST_OPTIONAL
#include <boost/version.hpp>
#if BOOST_VERSION < 106100
#error "This boost::optional version is not supported, use 1.61 or better"
#endif
#endif

#if CLI11_STD_OPTIONAL
#include <optional>
#endif
#if CLI11_EXPERIMENTAL_OPTIONAL
#include <experimental/optional>
#endif
#if CLI11_BOOST_OPTIONAL
#include <boost/optional.hpp>
#include <boost/optional/optional_io.hpp>
#endif


// From CLI/Version.hpp:



// From CLI/Macros.hpp:



// From CLI/Optional.hpp:

namespace CLI {

#if CLI11_STD_OPTIONAL
template <typename T> std::istream &operator>>(std::istream &in, std::optional<T> &val) {
    T v;
    in >> v;
    val = v;
    return in;
}
#endif

#if CLI11_EXPERIMENTAL_OPTIONAL
template <typename T> std::istream &operator>>(std::istream &in, std::experimental::optional<T> &val) {
    T v;
    in >> v;
    val = v;
    return in;
}
#endif

#if CLI11_BOOST_OPTIONAL
template <typename T> std::istream &operator>>(std::istream &in, boost::optional<T> &val) {
    T v;
    in >> v;
    val = v;
    return in;
}
#endif

// Export the best optional to the CLI namespace
#if CLI11_STD_OPTIONAL
using std::optional;
#elif CLI11_EXPERIMENTAL_OPTIONAL
using std::experimental::optional;
#elif CLI11_BOOST_OPTIONAL
using boost::optional;
#endif

// This is true if any optional is found
#if CLI11_STD_OPTIONAL || CLI11_EXPERIMENTAL_OPTIONAL || CLI11_BOOST_OPTIONAL
#define CLI11_OPTIONAL 1
#endif

} // namespace CLI

// From CLI/StringTools.hpp:

namespace CLI {

/// Include the items in this namespace to get free conversion of enums to/from streams.
/// (This is available inside CLI as well, so CLI11 will use this without a using statement).
namespace enums {

/// output streaming for enumerations
template <typename T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
std::ostream &operator<<(std::ostream &in, const T &item) {
    // make sure this is out of the detail namespace otherwise it won't be found when needed
    return in << static_cast<typename std::underlying_type<T>::type>(item);
}

/// input streaming for enumerations
template <typename T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
std::istream &operator>>(std::istream &in, T &item) {
    typename std::underlying_type<T>::type i;
    in >> i;
    item = static_cast<T>(i);
    return in;
}
} // namespace enums

/// Export to CLI namespace
using namespace enums;

namespace detail {

// 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) {
    std::vector<std::string> elems;
    // Check to see if empty string, give consistent result
    if(s.empty())
        elems.emplace_back();
    else {
        std::stringstream ss;
        ss.str(s);
        std::string item;
        while(std::getline(ss, item, delim)) {
            elems.push_back(item);
        }
    }
    return elems;
}
/// simple utility to convert various types to a string
template <typename T> inline std::string as_string(const T &v) {
    std::ostringstream s;
    s << v;
    return s.str();
}
// if the data type is already a string just forward it
template <typename T, typename = typename std::enable_if<std::is_constructible<std::string, T>::value>::type>
inline auto as_string(T &&v) -> decltype(std::forward<T>(v)) {
    return std::forward<T>(v);
}

/// Simple function to join a string
template <typename T> std::string join(const T &v, std::string delim = ",") {
    std::ostringstream s;
    auto beg = std::begin(v);
    auto end = std::end(v);
    if(beg != end)
        s << *beg++;
    while(beg != end) {
        s << delim << *beg++;
    }
    return s.str();
}

/// Simple function to join a string from processed elements
template <typename T,
          typename Callable,
          typename = typename std::enable_if<!std::is_constructible<std::string, Callable>::value>::type>
std::string join(const T &v, Callable func, std::string delim = ",") {
    std::ostringstream s;
    auto beg = std::begin(v);
    auto end = std::end(v);
    if(beg != end)
        s << func(*beg++);
    while(beg != end) {
        s << delim << func(*beg++);
    }
    return s.str();
}

/// 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++) {
        if(start > 0)
            s << delim;
        s << v[v.size() - start - 1];
    }
    return s.str();
}

// Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string

/// Trim whitespace from left of string
inline std::string &ltrim(std::string &str) {
    auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
    str.erase(str.begin(), it);
    return str;
}

/// Trim anything from left of string
inline std::string &ltrim(std::string &str, const std::string &filter) {
    auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
    str.erase(str.begin(), it);
    return str;
}

/// Trim whitespace from right of string
inline std::string &rtrim(std::string &str) {
    auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
    str.erase(it.base(), str.end());
    return str;
}

/// Trim anything from right of string
inline std::string &rtrim(std::string &str, const std::string &filter) {
    auto it =
        std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
    str.erase(it.base(), str.end());
    return str;
}

/// Trim whitespace from string
inline std::string &trim(std::string &str) { return ltrim(rtrim(str)); }

/// Trim anything from string
inline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); }

/// Make a copy of the string and then trim it
inline std::string trim_copy(const std::string &str) {
    std::string s = str;
    return trim(s);
}

/// 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) {
    name = "  " + name;
    out << std::setw(static_cast<int>(wid)) << std::left << name;
    if(!description.empty()) {
        if(name.length() >= wid)
            out << "\n" << std::setw(static_cast<int>(wid)) << "";
        for(const char c : description) {
            out.put(c);
            if(c == '\n') {
                out << std::setw(static_cast<int>(wid)) << "";
            }
        }
    }
    out << "\n";
    return out;
}

/// Verify the first character of an option
template <typename T> bool valid_first_char(T c) {
    return std::isalnum(c, std::locale()) || c == '_' || c == '?' || c == '@';
}

/// Verify following characters of an option
template <typename T> bool valid_later_char(T c) { return valid_first_char(c) || c == '.' || c == '-'; }

/// Verify an option name
inline bool valid_name_string(const std::string &str) {
    if(str.empty() || !valid_first_char(str[0]))
        return false;
    for(auto c : str.substr(1))
        if(!valid_later_char(c))
            return false;
    return true;
}

/// Verify that str consists of letters only
inline bool isalpha(const std::string &str) {
    return std::all_of(str.begin(), str.end(), [](char c) { return std::isalpha(c, std::locale()); });
}

/// Return a lower case version of a string
inline std::string to_lower(std::string str) {
    std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) {
        return std::tolower(x, std::locale());
    });
    return str;
}

/// remove underscores from a string
inline std::string remove_underscore(std::string str) {
    str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str));
    return 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;

    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length();
    }

    return str;
}

/// check if the flag definitions has possible false flags
inline bool has_default_flag_values(const std::string &flags) {
    return (flags.find_first_of("{!") != std::string::npos);
}

inline void remove_default_flag_values(std::string &flags) {
    auto loc = flags.find_first_of('{');
    while(loc != std::string::npos) {
        auto finish = flags.find_first_of("},", loc + 1);
        if((finish != std::string::npos) && (flags[finish] == '}')) {
            flags.erase(flags.begin() + static_cast<std::ptrdiff_t>(loc),
                        flags.begin() + static_cast<std::ptrdiff_t>(finish) + 1);
        }
        loc = flags.find_first_of('{', loc + 1);
    }
    flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end());
}

/// Check if a string is a member of a list of strings and optionally ignore case or ignore underscores
inline std::ptrdiff_t find_member(std::string name,
                                  const std::vector<std::string> names,
                                  bool ignore_case = false,
                                  bool ignore_underscore = false) {
    auto it = std::end(names);
    if(ignore_case) {
        if(ignore_underscore) {
            name = detail::to_lower(detail::remove_underscore(name));
            it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
                return detail::to_lower(detail::remove_underscore(local_name)) == name;
            });
        } else {
            name = detail::to_lower(name);
            it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
                return detail::to_lower(local_name) == name;
            });
        }

    } else if(ignore_underscore) {
        name = detail::remove_underscore(name);
        it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
            return detail::remove_underscore(local_name) == name;
        });
    } else
        it = std::find(std::begin(names), std::end(names), name);

    return (it != std::end(names)) ? (it - std::begin(names)) : (-1);
}

/// 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;
    while((start_pos = str.find(trigger, start_pos)) != std::string::npos) {
        start_pos = modify(str, start_pos);
    }
    return 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) {

    const std::string delims("\'\"`");
    auto find_ws = [](char ch) { return std::isspace<char>(ch, std::locale()); };
    trim(str);

    std::vector<std::string> output;
    bool embeddedQuote = false;
    char keyChar = ' ';
    while(!str.empty()) {
        if(delims.find_first_of(str[0]) != std::string::npos) {
            keyChar = str[0];
            auto end = str.find_first_of(keyChar, 1);
            while((end != std::string::npos) && (str[end - 1] == '\\')) { // deal with escaped quotes
                end = str.find_first_of(keyChar, end + 1);
                embeddedQuote = true;
            }
            if(end != std::string::npos) {
                output.push_back(str.substr(1, end - 1));
                str = str.substr(end + 1);
            } else {
                output.push_back(str.substr(1));
                str = "";
            }
        } else {
            auto it = std::find_if(std::begin(str), std::end(str), find_ws);
            if(it != std::end(str)) {
                std::string value = std::string(str.begin(), it);
                output.push_back(value);
                str = std::string(it, str.end());
            } else {
                output.push_back(str);
                str = "";
            }
        }
        // transform any embedded quotes into the regular character
        if(embeddedQuote) {
            output.back() = find_and_replace(output.back(), std::string("\\") + keyChar, std::string(1, keyChar));
            embeddedQuote = false;
        }
        trim(str);
    }
    return output;
}

/// Add a leader to the beginning of all new lines (nothing is added
/// 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) {
    std::string::size_type n = 0;
    while(n != std::string::npos && n < input.size()) {
        n = input.find('\n', n);
        if(n != std::string::npos) {
            input = input.substr(0, n + 1) + leader + input.substr(n + 1);
            n += leader.size();
        }
    }
    return input;
}

/// This function detects an equal or colon followed by an escaped quote after an argument
/// 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) {
    auto next = str[offset + 1];
    if((next == '\"') || (next == '\'') || (next == '`')) {
        auto astart = str.find_last_of("-/ \"\'`", offset - 1);
        if(astart != std::string::npos) {
            if(str[astart] == ((str[offset] == '=') ? '-' : '/'))
                str[offset] = ' '; // interpret this as a space so the split_up works properly
        }
    }
    return offset + 1;
}

/// Add quotes if the string contains spaces
inline std::string &add_quotes_if_needed(std::string &str) {
    if((str.front() != '"' && str.front() != '\'') || str.front() != str.back()) {
        char quote = str.find('"') < str.find('\'') ? '\'' : '"';
        if(str.find(' ') != std::string::npos) {
            str.insert(0, 1, quote);
            str.append(1, quote);
        }
    }
    return str;
}

} // namespace detail

} // namespace CLI

// From CLI/Error.hpp:

namespace CLI {

// Use one of these on all error classes.
// These are temporary and are undef'd at the end of this file.
#define CLI11_ERROR_DEF(parent, name)                                                                                  \
  protected:                                                                                                           \
    name(std::string ename, std::string msg, int exit_code) : parent(std::move(ename), std::move(msg), exit_code) {}   \
    name(std::string ename, std::string msg, ExitCodes exit_code)                                                      \
        : parent(std::move(ename), std::move(msg), exit_code) {}                                                       \
                                                                                                                       \
  public:                                                                                                              \
    name(std::string msg, ExitCodes exit_code) : parent(#name, std::move(msg), exit_code) {}                           \
    name(std::string msg, int exit_code) : parent(#name, std::move(msg), exit_code) {}

// This is added after the one above if a class is used directly and builds its own message
#define CLI11_ERROR_SIMPLE(name)                                                                                       \
    explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {}

/// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut,
/// int values from e.get_error_code().
enum class ExitCodes {
    Success = 0,
    IncorrectConstruction = 100,
    BadNameString,
    OptionAlreadyAdded,
    FileError,
    ConversionError,
    ValidationError,
    RequiredError,
    RequiresError,
    ExcludesError,
    ExtrasError,
    ConfigError,
    InvalidError,
    HorribleError,
    OptionNotFound,
    ArgumentMismatch,
    BaseClass = 127
};

// Error definitions

/// @defgroup error_group Errors
/// @brief Errors thrown by CLI11
///
/// These are the errors that can be thrown. Some of them, like CLI::Success, are not really errors.
/// @{

/// All errors derive from this one
class Error : public std::runtime_error {
    int actual_exit_code;
    std::string error_name{"Error"};

  public:
    int get_exit_code() const { return actual_exit_code; }

    std::string get_name() const { return error_name; }

    Error(std::string name, std::string msg, int exit_code = static_cast<int>(ExitCodes::BaseClass))
        : runtime_error(msg), actual_exit_code(exit_code), error_name(std::move(name)) {}

    Error(std::string name, std::string msg, ExitCodes exit_code) : Error(name, msg, static_cast<int>(exit_code)) {}
};

// Note: Using Error::Error constructors does not work on GCC 4.7

/// Construction errors (not in parsing)
class ConstructionError : public Error {
    CLI11_ERROR_DEF(Error, ConstructionError)
};

/// Thrown when an option is set to conflicting values (non-vector and multi args, for example)
class IncorrectConstruction : public ConstructionError {
    CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction)
    CLI11_ERROR_SIMPLE(IncorrectConstruction)
    static IncorrectConstruction PositionalFlag(std::string name) {
        return IncorrectConstruction(name + ": Flags cannot be positional");
    }
    static IncorrectConstruction Set0Opt(std::string name) {
        return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead");
    }
    static IncorrectConstruction SetFlag(std::string name) {
        return IncorrectConstruction(name + ": Cannot set an expected number for flags");
    }
    static IncorrectConstruction ChangeNotVector(std::string name) {
        return IncorrectConstruction(name + ": You can only change the expected arguments for vectors");
    }
    static IncorrectConstruction AfterMultiOpt(std::string name) {
        return IncorrectConstruction(
            name + ": You can't change expected arguments after you've changed the multi option policy!");
    }
    static IncorrectConstruction MissingOption(std::string name) {
        return IncorrectConstruction("Option " + name + " is not defined");
    }
    static IncorrectConstruction MultiOptionPolicy(std::string name) {
        return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options");
    }
};

/// Thrown on construction of a bad name
class BadNameString : public ConstructionError {
    CLI11_ERROR_DEF(ConstructionError, BadNameString)
    CLI11_ERROR_SIMPLE(BadNameString)
    static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); }
    static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); }
    static BadNameString DashesOnly(std::string name) {
        return BadNameString("Must have a name, not just dashes: " + name);
    }
    static BadNameString MultiPositionalNames(std::string name) {
        return BadNameString("Only one positional name allowed, remove: " + name);
    }
};

/// Thrown when an option already exists
class OptionAlreadyAdded : public ConstructionError {
    CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded)
    explicit OptionAlreadyAdded(std::string name)
        : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {}
    static OptionAlreadyAdded Requires(std::string name, std::string other) {
        return OptionAlreadyAdded(name + " requires " + other, ExitCodes::OptionAlreadyAdded);
    }
    static OptionAlreadyAdded Excludes(std::string name, std::string other) {
        return OptionAlreadyAdded(name + " excludes " + other, ExitCodes::OptionAlreadyAdded);
    }
};

// Parsing errors

/// Anything that can error in Parse
class ParseError : public Error {
    CLI11_ERROR_DEF(Error, ParseError)
};

// Not really "errors"

/// This is a successful completion on parsing, supposed to exit
class Success : public ParseError {
    CLI11_ERROR_DEF(ParseError, Success)
    Success() : Success("Successfully completed, should be caught and quit", ExitCodes::Success) {}
};

/// -h or --help on command line
class CallForHelp : public ParseError {
    CLI11_ERROR_DEF(ParseError, CallForHelp)
    CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
};

/// Usually something like --help-all on command line
class CallForAllHelp : public ParseError {
    CLI11_ERROR_DEF(ParseError, CallForAllHelp)
    CallForAllHelp()
        : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
};

/// Does not output a diagnostic in CLI11_PARSE, but allows to return from main() with a specific error code.
class RuntimeError : public ParseError {
    CLI11_ERROR_DEF(ParseError, RuntimeError)
    explicit RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {}
};

/// Thrown when parsing an INI file and it is missing
class FileError : public ParseError {
    CLI11_ERROR_DEF(ParseError, FileError)
    CLI11_ERROR_SIMPLE(FileError)
    static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); }
};

/// Thrown when conversion call back fails, such as when an int fails to coerce to a string
class ConversionError : public ParseError {
    CLI11_ERROR_DEF(ParseError, ConversionError)
    CLI11_ERROR_SIMPLE(ConversionError)
    ConversionError(std::string member, std::string name)
        : ConversionError("The value " + member + " is not an allowed value for " + name) {}
    ConversionError(std::string name, std::vector<std::string> results)
        : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {}
    static ConversionError TooManyInputsFlag(std::string name) {
        return ConversionError(name + ": too many inputs for a flag");
    }
    static ConversionError TrueFalse(std::string name) {
        return ConversionError(name + ": Should be true/false or a number");
    }
};

/// Thrown when validation of results fails
class ValidationError : public ParseError {
    CLI11_ERROR_DEF(ParseError, ValidationError)
    CLI11_ERROR_SIMPLE(ValidationError)
    explicit ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {}
};

/// Thrown when a required option is missing
class RequiredError : public ParseError {
    CLI11_ERROR_DEF(ParseError, RequiredError)
    explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {}
    static RequiredError Subcommand(size_t min_subcom) {
        if(min_subcom == 1)
            return RequiredError("A subcommand");
        else
            return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands",
                                 ExitCodes::RequiredError);
    }
    static RequiredError Option(size_t min_option, size_t max_option, size_t used, const std::string &option_list) {
        if((min_option == 1) && (max_option == 1) && (used == 0))
            return RequiredError("Exactly 1 option from [" + option_list + "]");
        else if((min_option == 1) && (max_option == 1) && (used > 1))
            return RequiredError("Exactly 1 option from [" + option_list + "] is required and " + std::to_string(used) +
                                     " were given",
                                 ExitCodes::RequiredError);
        else if((min_option == 1) && (used == 0))
            return RequiredError("At least 1 option from [" + option_list + "]");
        else if(used < min_option)
            return RequiredError("Requires at least " + std::to_string(min_option) + " options used and only " +
                                     std::to_string(used) + "were given from [" + option_list + "]",
                                 ExitCodes::RequiredError);
        else if(max_option == 1)
            return RequiredError("Requires at most 1 options be given from [" + option_list + "]",
                                 ExitCodes::RequiredError);
        else
            return RequiredError("Requires at most " + std::to_string(max_option) + " options be used and " +
                                     std::to_string(used) + "were given from [" + option_list + "]",
                                 ExitCodes::RequiredError);
    }
};

/// Thrown when the wrong number of arguments has been received
class ArgumentMismatch : public ParseError {
    CLI11_ERROR_DEF(ParseError, ArgumentMismatch)
    CLI11_ERROR_SIMPLE(ArgumentMismatch)
    ArgumentMismatch(std::string name, int expected, 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 TypedAtLeast(std::string name, int num, std::string type) {
        return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing");
    }
    static ArgumentMismatch FlagOverride(std::string name) {
        return ArgumentMismatch(name + " was given a disallowed flag override");
    }
};

/// Thrown when a requires option is missing
class RequiresError : public ParseError {
    CLI11_ERROR_DEF(ParseError, RequiresError)
    RequiresError(std::string curname, std::string subname)
        : RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {}
};

/// Thrown when an excludes option is present
class ExcludesError : public ParseError {
    CLI11_ERROR_DEF(ParseError, ExcludesError)
    ExcludesError(std::string curname, std::string subname)
        : ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {}
};

/// Thrown when too many positionals or options are found
class ExtrasError : public ParseError {
    CLI11_ERROR_DEF(ParseError, ExtrasError)
    explicit ExtrasError(std::vector<std::string> args)
        : ExtrasError((args.size() > 1 ? "The following arguments were not expected: "
                                       : "The following argument was not expected: ") +
                          detail::rjoin(args, " "),
                      ExitCodes::ExtrasError) {}
};

/// Thrown when extra values are found in an INI file
class ConfigError : public ParseError {
    CLI11_ERROR_DEF(ParseError, ConfigError)
    CLI11_ERROR_SIMPLE(ConfigError)
    static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); }
    static ConfigError NotConfigurable(std::string item) {
        return ConfigError(item + ": This option is not allowed in a configuration file");
    }
};

/// Thrown when validation fails before parsing
class InvalidError : public ParseError {
    CLI11_ERROR_DEF(ParseError, InvalidError)
    explicit InvalidError(std::string name)
        : InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) {
    }
};

/// This is just a safety check to verify selection and parsing match - you should not ever see it
/// Strings are directly added to this error, but again, it should never be seen.
class HorribleError : public ParseError {
    CLI11_ERROR_DEF(ParseError, HorribleError)
    CLI11_ERROR_SIMPLE(HorribleError)
};

// After parsing

/// Thrown when counting a non-existent option
class OptionNotFound : public Error {
    CLI11_ERROR_DEF(Error, OptionNotFound)
    explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {}
};

#undef CLI11_ERROR_DEF
#undef CLI11_ERROR_SIMPLE

/// @}

} // namespace CLI

// From CLI/TypeTools.hpp:

namespace CLI {

// Type tools

// Utilities for type enabling
namespace detail {
// Based generally on https://rmf.io/cxx11/almost-static-if
/// Simple empty scoped class
enum class enabler {};

/// An instance to use in EnableIf
constexpr enabler dummy = {};
} // namespace detail

/// A copy of enable_if_t from C++14, compatible with C++11.
///
/// We could check to see if C++14 is being used, but it does not hurt to redefine this
/// (even Google does this: https://github.com/google/skia/blob/master/include/private/SkTLogic.h)
/// It is not in the std namespace anyway, so no harm done.
template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;

/// A copy of std::void_t from C++17 (helper for C++11 and C++14)
template <typename... Ts> struct make_void { using type = void; };

/// A copy of std::void_t from C++17 - same reasoning as enable_if_t, it does not hurt to redefine
template <typename... Ts> using void_t = typename make_void<Ts...>::type;

/// A copy of std::conditional_t from C++14 - same reasoning as enable_if_t, it does not hurt to redefine
template <bool B, class T, class F> using conditional_t = typename std::conditional<B, T, F>::type;

/// Check to see if something is a vector (fail check by default)
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 bool (fail check by default)
template <typename T> struct is_bool : std::false_type {};

/// Check to see if something is bool (true if actually a bool)
template <> struct is_bool<bool> : std::true_type {};

/// Check to see if something is a shared pointer
template <typename T> struct is_shared_ptr : std::false_type {};

/// Check to see if something is a shared pointer (True if really a shared pointer)
template <typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};

/// Check to see if something is a shared pointer (True if really a shared pointer)
template <typename T> struct is_shared_ptr<const std::shared_ptr<T>> : std::true_type {};

/// Check to see if something is copyable pointer
template <typename T> struct is_copyable_ptr {
    static bool const value = is_shared_ptr<T>::value || std::is_pointer<T>::value;
};

/// This can be specialized to override the type deduction for IsMember.
template <typename T> struct IsMemberType { using type = T; };

/// The main custom type needed here is const char * should be a string.
template <> struct IsMemberType<const char *> { using type = std::string; };

namespace detail {

// These are utilities for IsMember

/// 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;
};

/// Combination of the element type and value type - remove pointer (including smart pointers) and get the value_type of
/// the container
template <typename T> struct element_value_type { using type = typename element_type<T>::type::value_type; };

/// Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost nothing.
template <typename T, typename _ = void> struct pair_adaptor : std::false_type {
    using value_type = typename T::value_type;
    using first_type = typename std::remove_const<value_type>::type;
    using second_type = typename std::remove_const<value_type>::type;

    /// Get the first value (really just the underlying value)
    template <typename Q> static auto first(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
        return std::forward<Q>(pair_value);
    }
    /// Get the second value (really just the underlying value)
    template <typename Q> static auto second(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
        return std::forward<Q>(pair_value);
    }
};

/// Adaptor for map-like structure (true version, must have key_type and mapped_type).
/// This wraps a mapped container in a few utilities access it in a general way.
template <typename T>
struct pair_adaptor<
    T,
    conditional_t<false, void_t<typename T::value_type::first_type, typename T::value_type::second_type>, void>>
    : std::true_type {
    using value_type = typename T::value_type;
    using first_type = typename std::remove_const<typename value_type::first_type>::type;
    using second_type = typename std::remove_const<typename value_type::second_type>::type;

    /// Get the first value (really just the underlying value)
    template <typename Q> static auto first(Q &&pair_value) -> decltype(std::get<0>(std::forward<Q>(pair_value))) {
        return std::get<0>(std::forward<Q>(pair_value));
    }
    /// Get the second value (really just the underlying value)
    template <typename Q> static auto second(Q &&pair_value) -> decltype(std::get<1>(std::forward<Q>(pair_value))) {
        return std::get<1>(std::forward<Q>(pair_value));
    }
};

// Check for streamability
// Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream

template <typename S, typename T> class is_streamable {
    template <typename SS, typename TT>
    static auto test(int) -> decltype(std::declval<SS &>() << std::declval<TT>(), std::true_type());

    template <typename, typename> static auto test(...) -> std::false_type;

  public:
    static const bool value = decltype(test<S, T>(0))::value;
};

/// Convert an object to a string (directly forward if this can become a string)
template <typename T, enable_if_t<std::is_constructible<std::string, T>::value, detail::enabler> = detail::dummy>
auto to_string(T &&value) -> decltype(std::forward<T>(value)) {
    return std::forward<T>(value);
}

/// Convert an object to a string (streaming must be supported for that type)
template <typename T,
          enable_if_t<!std::is_constructible<std::string, T>::value && is_streamable<std::stringstream, T>::value,
                      detail::enabler> = detail::dummy>
std::string to_string(T &&value) {
    std::stringstream stream;
    stream << value;
    return stream.str();
}

/// If conversion is not supported, return an empty string (streaming is not supported for that type)
template <typename T,
          enable_if_t<!std::is_constructible<std::string, T>::value && !is_streamable<std::stringstream, T>::value,
                      detail::enabler> = detail::dummy>
std::string to_string(T &&) {
    return std::string{};
}

// Type name print

/// Was going to be based on
///  http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template
/// But this is cleaner and works better in this case

template <typename T,
          enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
constexpr const char *type_name() {
    return "INT";
}

template <typename T,
          enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
constexpr const char *type_name() {
    return "UINT";
}

template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
constexpr const char *type_name() {
    return "FLOAT";
}

/// This one should not be used, since vector types print the internal type
template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
constexpr const char *type_name() {
    return "VECTOR";
}
/// Print name for enumeration types
template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
constexpr const char *type_name() {
    return "ENUM";
}

/// Print for all other types
template <typename T,
          enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value &&
                          !std::is_enum<T>::value,
                      detail::enabler> = detail::dummy>
constexpr const char *type_name() {
    return "TEXT";
}

// Lexical cast

/// Convert a flag into an integer value  typically binary flags
inline int64_t to_flag_value(std::string val) {
    static const std::string trueString("true");
    static const std::string falseString("false");
    if(val == trueString) {
        return 1;
    }
    if(val == falseString) {
        return -1;
    }
    val = detail::to_lower(val);
    int64_t ret;
    if(val.size() == 1) {
        switch(val[0]) {
        case '0':
        case 'f':
        case 'n':
        case '-':
            ret = -1;
            break;
        case '1':
        case 't':
        case 'y':
        case '+':
            ret = 1;
            break;
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            ret = val[0] - '0';
            break;
        default:
            throw std::invalid_argument("unrecognized character");
        }
        return ret;
    }
    if(val == trueString || val == "on" || val == "yes" || val == "enable") {
        ret = 1;
    } else if(val == falseString || val == "off" || val == "no" || val == "disable") {
        ret = -1;
    } else {
        ret = std::stoll(val);
    }
    return ret;
}

/// Signed integers
template <
    typename T,
    enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value && !is_bool<T>::value && !std::is_enum<T>::value,
                detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T &output) {
    try {
        size_t n = 0;
        long long output_ll = std::stoll(input, &n, 0);
        output = static_cast<T>(output_ll);
        return n == input.size() && static_cast<long long>(output) == output_ll;
    } catch(const std::invalid_argument &) {
        return false;
    } catch(const std::out_of_range &) {
        return false;
    }
}

/// Unsigned integers
template <typename T,
          enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value && !is_bool<T>::value, detail::enabler> =
              detail::dummy>
bool lexical_cast(std::string input, T &output) {
    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);
        output = static_cast<T>(output_ll);
        return n == input.size() && static_cast<unsigned long long>(output) == output_ll;
    } catch(const std::invalid_argument &) {
        return false;
    } catch(const std::out_of_range &) {
        return false;
    }
}

/// Boolean values
template <typename T, enable_if_t<is_bool<T>::value, detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T &output) {
    try {
        auto out = to_flag_value(input);
        output = (out > 0);
        return true;
    } catch(const std::invalid_argument &) {
        return false;
    }
}

/// Floats
template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T &output) {
    try {
        size_t n = 0;
        output = static_cast<T>(std::stold(input, &n));
        return n == input.size();
    } catch(const std::invalid_argument &) {
        return false;
    } catch(const std::out_of_range &) {
        return false;
    }
}

/// String and similar
template <typename T,
          enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
                          std::is_assignable<T &, std::string>::value,
                      detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T &output) {
    output = input;
    return true;
}

/// Enumerations
template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T &output) {
    typename std::underlying_type<T>::type val;
    bool retval = detail::lexical_cast(input, val);
    if(!retval) {
        return false;
    }
    output = static_cast<T>(val);
    return true;
}

/// Non-string parsable
template <typename T,
          enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
                          !std::is_assignable<T &, std::string>::value && !std::is_enum<T>::value,
                      detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T &output) {
    std::istringstream is;

    is.str(input);
    is >> output;
    return !is.fail() && !is.rdbuf()->in_avail();
}

/// 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
/// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most
/// common true and false strings then uses stoll to convert the rest for summing
template <typename T,
          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};
    for(auto &flag : flags) {
        count += detail::to_flag_value(flag);
    }
    output = (count > 0) ? static_cast<T>(count) : T{0};
}

/// 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
/// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most
/// common true and false strings then uses stoll to convert the rest for summing
template <typename T,
          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};
    for(auto &flag : flags) {
        count += detail::to_flag_value(flag);
    }
    output = static_cast<T>(count);
}

} // namespace detail
} // namespace CLI

// From CLI/Split.hpp:

namespace CLI {
namespace detail {

// Returns false if not a short option. Otherwise, sets opt name and rest and returns true
inline bool split_short(const std::string &current, std::string &name, std::string &rest) {
    if(current.size() > 1 && current[0] == '-' && valid_first_char(current[1])) {
        name = current.substr(1, 1);
        rest = current.substr(2);
        return true;
    } else
        return false;
}

// Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true
inline bool split_long(const std::string &current, std::string &name, std::string &value) {
    if(current.size() > 2 && current.substr(0, 2) == "--" && valid_first_char(current[2])) {
        auto loc = current.find_first_of('=');
        if(loc != std::string::npos) {
            name = current.substr(2, loc - 2);
            value = current.substr(loc + 1);
        } else {
            name = current.substr(2);
            value = "";
        }
        return true;
    } else
        return false;
}

// Returns false if not a windows style option. Otherwise, sets opt name and value and returns true
inline bool split_windows_style(const std::string &current, std::string &name, std::string &value) {
    if(current.size() > 1 && current[0] == '/' && valid_first_char(current[1])) {
        auto loc = current.find_first_of(':');
        if(loc != std::string::npos) {
            name = current.substr(1, loc - 1);
            value = current.substr(loc + 1);
        } else {
            name = current.substr(1);
            value = "";
        }
        return true;
    } else
        return false;
}

// 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;
    while((val = current.find(",")) != std::string::npos) {
        output.push_back(trim_copy(current.substr(0, val)));
        current = current.substr(val + 1);
    }
    output.push_back(trim_copy(current));
    return output;
}

/// extract default flag values either {def} or starting with a !
inline std::vector<std::pair<std::string, std::string>> get_default_flag_values(const std::string &str) {
    std::vector<std::string> flags = split_names(str);
    flags.erase(std::remove_if(flags.begin(),
                               flags.end(),
                               [](const std::string &name) {
                                   return ((name.empty()) || (!(((name.find_first_of('{') != std::string::npos) &&
                                                                 (name.back() == '}')) ||
                                                                (name[0] == '!'))));
                               }),
                flags.end());
    std::vector<std::pair<std::string, std::string>> output;
    output.reserve(flags.size());
    for(auto &flag : flags) {
        auto def_start = flag.find_first_of('{');
        std::string defval = "false";
        if((def_start != std::string::npos) && (flag.back() == '}')) {
            defval = flag.substr(def_start + 1);
            defval.pop_back();
            flag.erase(def_start, std::string::npos);
        }
        flag.erase(0, flag.find_first_not_of("-!"));
        output.emplace_back(flag, defval);
    }
    return output;
}

/// Get a vector of short names, one of long names, and a single name
inline std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>
get_names(const std::vector<std::string> &input) {

    std::vector<std::string> short_names;
    std::vector<std::string> long_names;
    std::string pos_name;

    for(std::string name : input) {
        if(name.length() == 0)
            continue;
        else if(name.length() > 1 && name[0] == '-' && name[1] != '-') {
            if(name.length() == 2 && valid_first_char(name[1]))
                short_names.emplace_back(1, name[1]);
            else
                throw BadNameString::OneCharName(name);
        } else if(name.length() > 2 && name.substr(0, 2) == "--") {
            name = name.substr(2);
            if(valid_name_string(name))
                long_names.push_back(name);
            else
                throw BadNameString::BadLongName(name);
        } else if(name == "-" || name == "--") {
            throw BadNameString::DashesOnly(name);
        } else {
            if(pos_name.length() > 0)
                throw BadNameString::MultiPositionalNames(name);
            pos_name = name;
        }
    }

    return std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>(
        short_names, long_names, pos_name);
}

} // namespace detail
} // namespace CLI

// From CLI/ConfigFwd.hpp:

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;

    /// This is the name
    std::string name;

    /// Listing of inputs
    std::vector<std::string> inputs;

    /// The list of parents and name joined by "."
    std::string fullname() const {
        std::vector<std::string> tmp = parents;
        tmp.emplace_back(name);
        return detail::join(tmp, ".");
    }
};

/// This class provides a converter for configuration files.
class Config {
  protected:
    std::vector<ConfigItem> items;

  public:
    /// Convert an app into a configuration
    virtual std::string to_config(const App *, bool, bool, std::string) const = 0;

    /// Convert a configuration into an app
    virtual std::vector<ConfigItem> from_config(std::istream &) const = 0;

    /// Get a flag value
    virtual std::string to_flag(const ConfigItem &item) const {
        if(item.inputs.size() == 1) {
            return item.inputs.at(0);
        }
        throw ConversionError::TooManyInputsFlag(item.fullname());
    }

    /// Parse a config file, throw an error (ParseError:ConfigParseError or FileError) on failure
    std::vector<ConfigItem> from_file(const std::string &name) {
        std::ifstream input{name};
        if(!input.good())
            throw FileError::Missing(name);

        return from_config(input);
    }

    /// Virtual destructor
    virtual ~Config() = default;
};

/// This converter works with INI files
class ConfigINI : public Config {
  public:
    std::string to_config(const App *, bool default_also, bool write_description, std::string prefix) const override;

    std::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;
    }
};

} // namespace CLI

// From CLI/Validators.hpp:

namespace CLI {

class Option;

/// @defgroup validator_group Validators

/// @brief Some validators that are provided
///
/// These are simple `std::string(const std::string&)` validators that are useful. They return
/// a string if the validation fails. A custom struct is provided, as well, with the same user
/// semantics, but with the ability to provide a new type name.
/// @{

///
class Validator {
  protected:
    /// 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.
    /// 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_;
    /// Enable for Validator to allow it to be disabled if need be
    bool active_{true};
    /// specify that a validator should not modify the input
    bool non_modifying_{false};

  public:
    Validator() = default;
    /// Construct a Validator with just the description string
    explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {}
    // Construct Validator from basic information
    Validator(std::function<std::string(std::string &)> op, std::string validator_desc, std::string validator_name = "")
        : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)),
          name_(std::move(validator_name)) {}
    /// Set the Validator operation function
    Validator &operation(std::function<std::string(std::string &)> op) {
        func_ = std::move(op);
        return *this;
    }
    /// This is the required operator for a Validator - provided to help
    /// users (CLI11 uses the member `func` directly)
    std::string operator()(std::string &str) const {
        std::string retstring;
        if(active_) {
            if(non_modifying_) {
                std::string value = str;
                retstring = func_(value);
            } else {
                retstring = func_(str);
            }
        }
        return retstring;
    };

    /// This is the required operator for a Validator - provided to help
    /// users (CLI11 uses the member `func` directly)
    std::string operator()(const std::string &str) const {
        std::string value = str;
        return (active_) ? func_(value) : std::string{};
    };

    /// Specify the type string
    Validator &description(std::string validator_desc) {
        desc_function_ = [validator_desc]() { return validator_desc; };
        return *this;
    }
    /// Generate type description information for the Validator
    std::string get_description() const {
        if(active_) {
            return desc_function_();
        }
        return std::string{};
    }
    /// Specify the type string
    Validator &name(std::string validator_name) {
        name_ = std::move(validator_name);
        return *this;
    }
    /// Get the name of the Validator
    const std::string &get_name() const { return name_; }
    /// Specify whether the Validator is active or not
    Validator &active(bool active_val = true) {
        active_ = active_val;
        return *this;
    }

    /// Specify whether the Validator can be modifying or not
    Validator &non_modifying(bool no_modify = true) {
        non_modifying_ = no_modify;
        return *this;
    }

    /// Get a boolean if the validator is active
    bool get_active() const { return active_; }

    /// Get a boolean if the validator is allowed to modify the input returns true if it can modify the input
    bool get_modifying() const { return !non_modifying_; }

    /// Combining validators is a new validator. Type comes from left validator if function, otherwise only set if the
    /// same.
    Validator operator&(const Validator &other) const {
        Validator newval;

        newval._merge_description(*this, other, " AND ");

        // Give references (will make a copy in lambda function)
        const std::function<std::string(std::string & filename)> &f1 = func_;
        const std::function<std::string(std::string & filename)> &f2 = other.func_;

        newval.func_ = [f1, f2](std::string &input) {
            std::string s1 = f1(input);
            std::string s2 = f2(input);
            if(!s1.empty() && !s2.empty())
                return std::string("(") + s1 + ") AND (" + s2 + ")";
            else
                return s1 + s2;
        };

        newval.active_ = (active_ & other.active_);
        return newval;
    }

    /// Combining validators is a new validator. Type comes from left validator if function, otherwise only set if the
    /// same.
    Validator operator|(const Validator &other) const {
        Validator newval;

        newval._merge_description(*this, other, " OR ");

        // Give references (will make a copy in lambda function)
        const std::function<std::string(std::string &)> &f1 = func_;
        const std::function<std::string(std::string &)> &f2 = other.func_;

        newval.func_ = [f1, f2](std::string &input) {
            std::string s1 = f1(input);
            std::string s2 = f2(input);
            if(s1.empty() || s2.empty())
                return std::string();
            else
                return std::string("(") + s1 + ") OR (" + s2 + ")";
        };
        newval.active_ = (active_ & other.active_);
        return newval;
    }

    /// Create a validator that fails when a given validator succeeds
    Validator operator!() const {
        Validator newval;
        const std::function<std::string()> &dfunc1 = desc_function_;
        newval.desc_function_ = [dfunc1]() {
            auto str = dfunc1();
            return (!str.empty()) ? std::string("NOT ") + str : std::string{};
        };
        // Give references (will make a copy in lambda function)
        const std::function<std::string(std::string & res)> &f1 = func_;

        newval.func_ = [f1, dfunc1](std::string &test) -> std::string {
            std::string s1 = f1(test);
            if(s1.empty()) {
                return std::string("check ") + dfunc1() + " succeeded improperly";
            } else
                return std::string{};
        };
        newval.active_ = active_;
        return newval;
    }

  private:
    void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger) {

        const std::function<std::string()> &dfunc1 = val1.desc_function_;
        const std::function<std::string()> &dfunc2 = val2.desc_function_;

        desc_function_ = [=]() {
            std::string f1 = dfunc1();
            std::string f2 = dfunc2();
            if((f1.empty()) || (f2.empty())) {
                return f1 + f2;
            }
            return std::string("(") + f1 + ")" + merger + "(" + f2 + ")";
        };
    }
};

/// Class wrapping some of the accessors of Validator
class CustomValidator : public Validator {
  public:
};
// The implementation of the built in validators is using the Validator class;
// the user is only expected to use the const (static) versions (since there's no setup).
// Therefore, this is in detail.
namespace detail {

/// 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) {
                return "File does not exist: " + filename;
            } else if(is_dir) {
                return "File is actually a directory: " + filename;
            }
            return std::string();
        };
    }
};

/// Check for an existing directory (returns error message if check fails)
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) {
                return "Directory does not exist: " + filename;
            } else if(!is_dir) {
                return "Directory is actually a file: " + filename;
            }
            return std::string();
        };
    }
};

/// Check for an existing path
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) {
                return "Path does not exist: " + filename;
            }
            return std::string();
        };
    }
};

/// Check for an non-existing path
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) {
                return "Path already exists: " + filename;
            }
            return std::string();
        };
    }
};

/// Validate the given string is a legal ipv4 address
class IPV4Validator : public Validator {
  public:
    IPV4Validator() : Validator("IPV4") {
        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;
            }
            int num;
            bool retval = true;
            for(const auto &var : result) {
                retval &= detail::lexical_cast(var, num);
                if(!retval) {
                    return "Failed parsing number " + var;
                }
                if(num < 0 || num > 255) {
                    return "Each IP number must be between 0 and 255 " + var;
                }
            }
            return std::string();
        };
    }
};

/// Validate the argument is a number and greater than or equal to 0
class PositiveNumber : public Validator {
  public:
    PositiveNumber() : Validator("POSITIVE") {
        func_ = [](std::string &number_str) {
            int number;
            if(!detail::lexical_cast(number_str, number)) {
                return "Failed parsing number " + number_str;
            }
            if(number < 0) {
                return "Number less then 0 " + number_str;
            }
            return std::string();
        };
    }
};

/// Validate the argument is a number and greater than or equal to 0
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();
        };
    }
};

} // namespace detail

// Static is not needed here, because global const implies static.

/// Check for existing file (returns error message if check fails)
const detail::ExistingFileValidator ExistingFile;

/// Check for an existing directory (returns error message if check fails)
const detail::ExistingDirectoryValidator ExistingDirectory;

/// Check for an existing path
const detail::ExistingPathValidator ExistingPath;

/// Check for an non-existing path
const detail::NonexistentPathValidator NonexistentPath;

/// Check for an IP4 address
const detail::IPV4Validator ValidIPV4;

/// Check for a positive number
const detail::PositiveNumber PositiveNumber;

/// Check for a number
const detail::Number Number;

/// Produce a range (factory). Min and max are inclusive.
class Range : public Validator {
  public:
    /// This produces a range with min and max inclusive.
    ///
    /// Note that the constructor is templated, but the struct is not, so C++17 is not
    /// needed to provide nice syntax for Range(a,b).
    template <typename T> Range(T min, T max) {
        std::stringstream out;
        out << detail::type_name<T>() << " in [" << min << " - " << max << "]";
        description(out.str());

        func_ = [min, max](std::string &input) {
            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();
        };
    }

    /// Range of one value is 0 to value
    template <typename T> explicit Range(T max) : Range(static_cast<T>(0), max) {}
};

/// Produce a bounded range (factory). Min and max are inclusive.
class Bound : public Validator {
  public:
    /// This bounds a value with min and max inclusive.
    ///
    /// Note that the constructor is templated, but the struct is not, so C++17 is not
    /// needed to provide nice syntax for Range(a,b).
    template <typename T> Bound(T min, T max) {
        std::stringstream out;
        out << detail::type_name<T>() << " bounded to [" << min << " - " << max << "]";
        description(out.str());

        func_ = [min, max](std::string &input) {
            T val;
            bool converted = detail::lexical_cast(input, val);
            if(!converted) {
                return "Value " + input + " could not be converted";
            }
            if(val < min)
                input = detail::as_string(min);
            else if(val > max)
                input = detail::as_string(max);

            return std::string();
        };
    }

    /// Range of one value is 0 to value
    template <typename T> explicit Bound(T max) : Bound(static_cast<T>(0), max) {}
};

namespace detail {
template <typename T,
          enable_if_t<is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
auto smart_deref(T value) -> decltype(*value) {
    return *value;
}

template <
    typename T,
    enable_if_t<!is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
typename std::remove_reference<T>::type &smart_deref(T &value) {
    return value;
}
/// Generate a string representation of a set
template <typename T> std::string generate_set(const T &set) {
    using element_t = typename detail::element_type<T>::type;
    using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
    std::string out(1, '{');
    out.append(detail::join(detail::smart_deref(set),
                            [](const iteration_type_t &v) { return detail::pair_adaptor<element_t>::first(v); },
                            ","));
    out.push_back('}');
    return out;
}

/// Generate a string representation of a map
template <typename T> std::string generate_map(const T &map, bool key_only = false) {
    using element_t = typename detail::element_type<T>::type;
    using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
    std::string out(1, '{');
    out.append(detail::join(detail::smart_deref(map),
                            [key_only](const iteration_type_t &v) {
                                auto res = detail::as_string(detail::pair_adaptor<element_t>::first(v));
                                if(!key_only) {
                                    res += "->" + detail::as_string(detail::pair_adaptor<element_t>::second(v));
                                }
                                return res;
                            },
                            ","));
    out.push_back('}');
    return out;
}

template <typename> struct sfinae_true : std::true_type {};
/// Function to check for the existence of a member find function which presumably is more efficient than looping over
/// everything
template <typename T, typename V>
static auto test_find(int) -> sfinae_true<decltype(std::declval<T>().find(std::declval<V>()))>;
template <typename, typename V> static auto test_find(long) -> std::false_type;

template <typename T, typename V> struct has_find : decltype(test_find<T, V>(0)) {};

/// A search function
template <typename T, typename V, enable_if_t<!has_find<T, V>::value, detail::enabler> = detail::dummy>
auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
    using element_t = typename detail::element_type<T>::type;
    auto &setref = detail::smart_deref(set);
    auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) {
        return (detail::pair_adaptor<element_t>::first(v) == val);
    });
    return {(it != std::end(setref)), it};
}

/// A search function that uses the built in find function
template <typename T, typename V, enable_if_t<has_find<T, V>::value, detail::enabler> = detail::dummy>
auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
    auto &setref = detail::smart_deref(set);
    auto it = setref.find(val);
    return {(it != std::end(setref)), it};
}

/// A search function with a filter function
template <typename T, typename V>
auto search(const T &set, const V &val, const std::function<V(V)> &filter_function)
    -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
    using element_t = typename detail::element_type<T>::type;
    // do the potentially faster first search
    auto res = search(set, val);
    if((res.first) || (!(filter_function))) {
        return res;
    }
    // if we haven't found it do the longer linear search with all the element translations
    auto &setref = detail::smart_deref(set);
    auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) {
        V a = detail::pair_adaptor<element_t>::first(v);
        a = filter_function(a);
        return (a == val);
    });
    return {(it != std::end(setref)), it};
}

/// Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise.
template <typename T> typename std::enable_if<std::is_integral<T>::value, bool>::type checked_multiply(T &a, T b) {
    if(a == 0 || b == 0) {
        a *= b;
        return true;
    }
    T c = a * b;
    if(c / a != b) {
        return false;
    }
    a = c;
    return true;
}

/// Performs a *= b; if it doesn't equal infinity. Returns false otherwise.
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type checked_multiply(T &a, T b) {
    T c = a * b;
    if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) {
        return false;
    }
    a = c;
    return true;
}

} // namespace detail
/// Verify items are in a set
class IsMember : public Validator {
  public:
    using filter_fn_t = std::function<std::string(std::string)>;

    /// 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::vector<T>(values), std::forward<Args>(args)...) {}

    /// This checks to see if an item is in a set (empty function)
    template <typename T> explicit IsMember(T &&set) : IsMember(std::forward<T>(set), nullptr) {}

    /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter
    /// both sides of the comparison before computing the comparison.
    template <typename T, typename F> explicit IsMember(T set, F filter_function) {

        // Get the type of the contained item - requires a container have ::value_type
        // if the type does not have first_type and second_type, these are both value_type
        using element_t = typename detail::element_type<T>::type;            // Removes (smart) pointers if needed
        using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map

        using local_item_t = typename IsMemberType<item_t>::type; // This will convert bad types to good ones
                                                                  // (const char * to std::string)

        // Make a local copy of the filter function, using a std::function if not one already
        std::function<local_item_t(local_item_t)> filter_fn = filter_function;

        // This is the type name for help, it will take the current version of the set contents
        desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); };

        // This is the function that validates
        // It stores a copy of the set pointer-like, so shared_ptr will stay alive
        func_ = [set, filter_fn](std::string &input) {
            local_item_t b;
            if(!detail::lexical_cast(input, b)) {
                throw ValidationError(input); // name is added later
            }
            if(filter_fn) {
                b = filter_fn(b);
            }
            auto res = detail::search(set, b, filter_fn);
            if(res.first) {
                // Make sure the version in the input string is identical to the one in the set
                if(filter_fn) {
                    input = detail::as_string(detail::pair_adaptor<element_t>::first(*(res.second)));
                }

                // Return empty error string (success)
                return std::string{};
            }

            // If you reach this point, the result was not found
            std::string out(" not in ");
            out += detail::generate_set(detail::smart_deref(set));
            return out;
        };
    }

    /// You can pass in as many filter functions as you like, they nest (string only currently)
    template <typename T, typename... Args>
    IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
        : IsMember(std::forward<T>(set),
                   [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
                   other...) {}
};

/// definition of the default transformation object
template <typename T> using TransformPairs = std::vector<std::pair<std::string, T>>;

/// Translate named items to other or a value set
class Transformer : public Validator {
  public:
    using filter_fn_t = std::function<std::string(std::string)>;

    /// This allows in-place construction
    template <typename... Args>
    explicit 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
    template <typename T> explicit Transformer(T &&mapping) : Transformer(std::forward<T>(mapping), nullptr) {}

    /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter
    /// both sides of the comparison before computing the comparison.
    template <typename T, typename F> explicit Transformer(T mapping, F filter_function) {

        static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
                      "mapping must produce value pairs");
        // Get the type of the contained item - requires a container have ::value_type
        // if the type does not have first_type and second_type, these are both value_type
        using element_t = typename detail::element_type<T>::type;            // Removes (smart) pointers if needed
        using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
        using local_item_t = typename IsMemberType<item_t>::type;            // This will convert bad types to good ones
                                                                             // (const char * to std::string)

        // Make a local copy of the filter function, using a std::function if not one already
        std::function<local_item_t(local_item_t)> filter_fn = filter_function;

        // This is the type name for help, it will take the current version of the set contents
        desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); };

        func_ = [mapping, filter_fn](std::string &input) {
            local_item_t b;
            if(!detail::lexical_cast(input, b)) {
                return std::string();
                // there is no possible way we can match anything in the mapping if we can't convert so just return
            }
            if(filter_fn) {
                b = filter_fn(b);
            }
            auto res = detail::search(mapping, b, filter_fn);
            if(res.first) {
                input = detail::as_string(detail::pair_adaptor<element_t>::second(*res.second));
            }
            return std::string{};
        };
    }

    /// You can pass in as many filter functions as you like, they nest
    template <typename T, typename... Args>
    Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
        : Transformer(std::forward<T>(mapping),
                      [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
                      other...) {}
};

/// translate named items to other or a value set
class CheckedTransformer : public Validator {
  public:
    using filter_fn_t = std::function<std::string(std::string)>;

    /// This allows in-place construction
    template <typename... Args>
    explicit 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
    template <typename T> explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {}

    /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter
    /// both sides of the comparison before computing the comparison.
    template <typename T, typename F> explicit CheckedTransformer(T mapping, F filter_function) {

        static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
                      "mapping must produce value pairs");
        // Get the type of the contained item - requires a container have ::value_type
        // if the type does not have first_type and second_type, these are both value_type
        using element_t = typename detail::element_type<T>::type;            // Removes (smart) pointers if needed
        using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
        using local_item_t = typename IsMemberType<item_t>::type;            // This will convert bad types to good ones
                                                                             // (const char * to std::string)
        using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair //
                                                                                       // the type of the object pair

        // Make a local copy of the filter function, using a std::function if not one already
        std::function<local_item_t(local_item_t)> filter_fn = filter_function;

        auto tfunc = [mapping]() {
            std::string out("value in ");
            out += detail::generate_map(detail::smart_deref(mapping)) + " OR {";
            out += detail::join(
                detail::smart_deref(mapping),
                [](const iteration_type_t &v) { return detail::as_string(detail::pair_adaptor<element_t>::second(v)); },
                ",");
            out.push_back('}');
            return out;
        };

        desc_function_ = tfunc;

        func_ = [mapping, tfunc, filter_fn](std::string &input) {
            local_item_t b;
            bool converted = detail::lexical_cast(input, b);
            if(converted) {
                if(filter_fn) {
                    b = filter_fn(b);
                }
                auto res = detail::search(mapping, b, filter_fn);
                if(res.first) {
                    input = detail::as_string(detail::pair_adaptor<element_t>::second(*res.second));
                    return std::string{};
                }
            }
            for(const auto &v : detail::smart_deref(mapping)) {
                auto output_string = detail::as_string(detail::pair_adaptor<element_t>::second(v));
                if(output_string == input) {
                    return std::string();
                }
            }

            return "Check " + input + " " + tfunc() + " FAILED";
        };
    }

    /// You can pass in as many filter functions as you like, they nest
    template <typename T, typename... Args>
    CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
        : CheckedTransformer(std::forward<T>(mapping),
                             [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
                             other...) {}
};

/// Helper function to allow ignore_case to be passed to IsMember or Transform
inline std::string ignore_case(std::string item) { return detail::to_lower(item); }

/// Helper function to allow ignore_underscore to be passed to IsMember or Transform
inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); }

/// Helper function to allow checks to ignore spaces to be passed to IsMember or Transform
inline std::string ignore_space(std::string item) {
    item.erase(std::remove(std::begin(item), std::end(item), ' '), std::end(item));
    item.erase(std::remove(std::begin(item), std::end(item), '\t'), std::end(item));
    return item;
}

/// Multiply a number by a factor using given mapping.
/// Can be used to write transforms for SIZE or DURATION inputs.
///
/// Example:
///   With mapping = `{"b"->1, "kb"->1024, "mb"->1024*1024}`
///   one can recognize inputs like "100", "12kb", "100 MB",
///   that will be automatically transformed to 100, 14448, 104857600.
///
/// Output number type matches the type in the provided mapping.
/// Therefore, if it is required to interpret real inputs like "0.42 s",
/// the mapping should be of a type <string, float> or <string, double>.
class AsNumberWithUnit : public Validator {
  public:
    /// Adjust AsNumberWithUnit behavior.
    /// CASE_SENSITIVE/CASE_INSENSITIVE controls how units are matched.
    /// UNIT_OPTIONAL/UNIT_REQUIRED throws ValidationError
    ///   if UNIT_REQUIRED is set and unit literal is not found.
    enum Options {
        CASE_SENSITIVE = 0,
        CASE_INSENSITIVE = 1,
        UNIT_OPTIONAL = 0,
        UNIT_REQUIRED = 2,
        DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL
    };

    template <typename Number>
    explicit AsNumberWithUnit(std::map<std::string, Number> mapping,
                              Options opts = DEFAULT,
                              const std::string &unit_name = "UNIT") {
        description(generate_description<Number>(unit_name, opts));
        validate_mapping(mapping, opts);

        // transform function
        func_ = [mapping, opts](std::string &input) -> std::string {
            Number num;

            detail::rtrim(input);
            if(input.empty()) {
                throw ValidationError("Input is empty");
            }

            // Find split position between number and prefix
            auto unit_begin = input.end();
            while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) {
                --unit_begin;
            }

            std::string unit{unit_begin, input.end()};
            input.resize(static_cast<size_t>(std::distance(input.begin(), unit_begin)));
            detail::trim(input);

            if(opts & UNIT_REQUIRED && unit.empty()) {
                throw ValidationError("Missing mandatory unit");
            }
            if(opts & CASE_INSENSITIVE) {
                unit = detail::to_lower(unit);
            }

            bool converted = detail::lexical_cast(input, num);
            if(!converted) {
                throw ValidationError("Value " + input + " could not be converted to " + detail::type_name<Number>());
            }

            if(unit.empty()) {
                // No need to modify input if no unit passed
                return {};
            }

            // find corresponding factor
            auto it = mapping.find(unit);
            if(it == mapping.end()) {
                throw ValidationError(unit +
                                      " unit not recognized. "
                                      "Allowed values: " +
                                      detail::generate_map(mapping, true));
            }

            // perform safe multiplication
            bool ok = detail::checked_multiply(num, it->second);
            if(!ok) {
                throw ValidationError(detail::as_string(num) + " multiplied by " + unit +
                                      " factor would cause number overflow. Use smaller value.");
            }
            input = detail::as_string(num);

            return {};
        };
    }

  private:
    /// Check that mapping contains valid units.
    /// Update mapping for CASE_INSENSITIVE mode.
    template <typename Number> static void validate_mapping(std::map<std::string, Number> &mapping, Options opts) {
        for(auto &kv : mapping) {
            if(kv.first.empty()) {
                throw ValidationError("Unit must not be empty.");
            }
            if(!detail::isalpha(kv.first)) {
                throw ValidationError("Unit must contain only letters.");
            }
        }

        // make all units lowercase if CASE_INSENSITIVE
        if(opts & CASE_INSENSITIVE) {
            std::map<std::string, Number> lower_mapping;
            for(auto &kv : mapping) {
                auto s = detail::to_lower(kv.first);
                if(lower_mapping.count(s)) {
                    throw ValidationError("Several matching lowercase unit representations are found: " + s);
                }
                lower_mapping[detail::to_lower(kv.first)] = kv.second;
            }
            mapping = std::move(lower_mapping);
        }
    }

    /// Generate description like this: NUMBER [UNIT]
    template <typename Number> static std::string generate_description(const std::string &name, Options opts) {
        std::stringstream out;
        out << detail::type_name<Number>() << ' ';
        if(opts & UNIT_REQUIRED) {
            out << name;
        } else {
            out << '[' << name << ']';
        }
        return out.str();
    }
};

/// Converts a human-readable size string (with unit literal) to uin64_t size.
/// Example:
///   "100" => 100
///   "1 b" => 100
///   "10Kb" => 10240 // you can configure this to be interpreted as kilobyte (*1000) or kibibyte (*1024)
///   "10 KB" => 10240
///   "10 kb" => 10240
///   "10 kib" => 10240 // *i, *ib are always interpreted as *bibyte (*1024)
///   "10kb" => 10240
///   "2 MB" => 2097152
///   "2 EiB" => 2^61 // Units up to exibyte are supported
class AsSizeValue : public AsNumberWithUnit {
  public:
    using result_t = uint64_t;

    /// If kb_is_1000 is true,
    /// interpret 'kb', 'k' as 1000 and 'kib', 'ki' as 1024
    /// (same applies to higher order units as well).
    /// Otherwise, interpret all literals as factors of 1024.
    /// The first option is formally correct, but
    /// the second interpretation is more wide-spread
    /// (see https://en.wikipedia.org/wiki/Binary_prefix).
    explicit AsSizeValue(bool kb_is_1000) : AsNumberWithUnit(get_mapping(kb_is_1000)) {
        if(kb_is_1000) {
            description("SIZE [b, kb(=1000b), kib(=1024b), ...]");
        } else {
            description("SIZE [b, kb(=1024b), ...]");
        }
    }

  private:
    /// Get <size unit, factor> mapping
    static std::map<std::string, result_t> init_mapping(bool kb_is_1000) {
        std::map<std::string, result_t> m;
        result_t k_factor = kb_is_1000 ? 1000 : 1024;
        result_t ki_factor = 1024;
        result_t k = 1;
        result_t ki = 1;
        m["b"] = 1;
        for(std::string p : {"k", "m", "g", "t", "p", "e"}) {
            k *= k_factor;
            ki *= ki_factor;
            m[p] = k;
            m[p + "b"] = k;
            m[p + "i"] = ki;
            m[p + "ib"] = ki;
        }
        return m;
    }

    /// Cache calculated mapping
    static std::map<std::string, result_t> get_mapping(bool kb_is_1000) {
        if(kb_is_1000) {
            static auto m = init_mapping(true);
            return m;
        } else {
            static auto m = init_mapping(false);
            return m;
        }
    }
};

namespace detail {
/// Split a string into a program name and command line arguments
/// the string is assumed to contain a file name followed by other arguments
/// the return value contains is a pair with the first argument containing the program name and the second
/// everything else.
inline std::pair<std::string, std::string> split_program_name(std::string commandline) {
    // try to determine the programName
    std::pair<std::string, std::string> vals;
    trim(commandline);
    auto esp = commandline.find_first_of(' ', 1);
    while(!ExistingFile(commandline.substr(0, esp)).empty()) {
        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
            // program name
            esp = commandline.find_first_of(' ', 1);
            break;
        }
    }
    vals.first = commandline.substr(0, esp);
    rtrim(vals.first);
    // strip the program name
    vals.second = (esp != std::string::npos) ? commandline.substr(esp + 1) : std::string{};
    ltrim(vals.second);
    return vals;
}

} // namespace detail
/// @}

} // namespace CLI

// From CLI/FormatterFwd.hpp:

namespace CLI {

class Option;
class App;

/// This enum signifies the type of help requested
///
/// This is passed in by App; all user classes must accept this as
/// the second argument.

enum class AppFormatMode {
    Normal, //< The normal, detailed help
    All,    //< A fully expanded help
    Sub,    //< Used when printed as part of expanded subcommand
};

/// This is the minimum requirements to run a formatter.
///
/// A user can subclass this is if they do not care at all
/// about the structure in CLI::Formatter.
class FormatterBase {
  protected:
    /// @name Options
    ///@{

    /// The width of the first column
    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_;

    ///@}
    /// @name Basic
    ///@{

  public:
    FormatterBase() = default;
    FormatterBase(const FormatterBase &) = default;
    FormatterBase(FormatterBase &&) = default;

    /// Adding a destructor in this form to work around bug in GCC 4.7
    virtual ~FormatterBase() noexcept {} // NOLINT(modernize-use-equals-default)

    /// This is the key method that puts together help
    virtual std::string make_help(const App *, std::string, AppFormatMode) const = 0;

    ///@}
    /// @name Setters
    ///@{

    /// Set the "REQUIRED" label
    void label(std::string key, std::string val) { labels_[key] = val; }

    /// Set the column width
    void column_width(size_t val) { column_width_ = val; }

    ///@}
    /// @name Getters
    ///@{

    /// Get the current value of a name (REQUIRED, etc.)
    std::string get_label(std::string key) const {
        if(labels_.find(key) == labels_.end())
            return key;
        else
            return labels_.at(key);
    }

    /// Get the current column width
    size_t get_column_width() const { return column_width_; }

    ///@}
};

/// This is a specialty override for lambda functions
class FormatterLambda final : public FormatterBase {
    using funct_t = std::function<std::string(const App *, std::string, AppFormatMode)>;

    /// The lambda to hold and run
    funct_t lambda_;

  public:
    /// Create a FormatterLambda with a lambda function
    explicit FormatterLambda(funct_t funct) : lambda_(std::move(funct)) {}

    /// Adding a destructor (mostly to make GCC 4.7 happy)
    ~FormatterLambda() noexcept override {} // NOLINT(modernize-use-equals-default)

    /// This will simply call the lambda function
    std::string make_help(const App *app, std::string name, AppFormatMode mode) const override {
        return lambda_(app, name, mode);
    }
};

/// This is the default Formatter for CLI11. It pretty prints help output, and is broken into quite a few
/// overridable methods, to be highly customizable with minimal effort.
class Formatter : public FormatterBase {
  public:
    Formatter() = default;
    Formatter(const Formatter &) = default;
    Formatter(Formatter &&) = default;

    /// @name Overridables
    ///@{

    /// This prints out a group of options with title
    ///
    virtual std::string make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const;

    /// This prints out just the positionals "group"
    virtual std::string make_positionals(const App *app) const;

    /// This prints out all the groups of options
    std::string make_groups(const App *app, AppFormatMode mode) const;

    /// This prints out all the subcommands
    virtual std::string make_subcommands(const App *app, AppFormatMode mode) const;

    /// This prints out a subcommand
    virtual std::string make_subcommand(const App *sub) const;

    /// This prints out a subcommand in help-all
    virtual std::string make_expanded(const App *sub) const;

    /// This prints out all the groups of options
    virtual std::string make_footer(const App *app) const;

    /// This displays the description line
    virtual std::string make_description(const App *app) const;

    /// This displays the usage line
    virtual std::string make_usage(const App *app, std::string name) const;

    /// This puts everything together
    std::string make_help(const App *, std::string, AppFormatMode) const override;

    ///@}
    /// @name Options
    ///@{

    /// This prints out an option help line, either positional or optional form
    virtual std::string make_option(const Option *opt, bool is_positional) const {
        std::stringstream out;
        detail::format_help(
            out, make_option_name(opt, is_positional) + make_option_opts(opt), make_option_desc(opt), column_width_);
        return out.str();
    }

    /// @brief This is the name part of an option, Default: left column
    virtual std::string make_option_name(const Option *, bool) const;

    /// @brief This is the options part of the name, Default: combined into left column
    virtual std::string make_option_opts(const Option *) const;

    /// @brief This is the description. Default: Right column, on new line if left column too large
    virtual std::string make_option_desc(const Option *) const;

    /// @brief This is used to print the name on the USAGE line
    virtual std::string make_option_usage(const Option *opt) const;

    ///@}
};

} // namespace CLI

// From CLI/Option.hpp:

namespace CLI {

using results_t = std::vector<std::string>;
using callback_t = std::function<bool(results_t)>;

class Option;
class App;

using Option_p = std::unique_ptr<Option>;

enum class MultiOptionPolicy : char { Throw, TakeLast, TakeFirst, Join };

/// 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.
template <typename CRTP> class OptionBase {
    friend App;

  protected:
    /// The group membership
    std::string group_ = std::string("Options");

    /// True if this is a required option
    bool required_{false};

    /// Ignore the case when matching (option, not value)
    bool ignore_case_{false};

    /// Ignore underscores when matching (option, not value)
    bool ignore_underscore_{false};

    /// Allow this option to be given in a configuration file
    bool configurable_{true};

    /// Disable overriding flag values with '=value'
    bool disable_flag_override_{false};

    /// Specify a delimiter character for vector arguments
    char delimiter_{'\0'};

    /// Automatically capture default value
    bool always_capture_default_{false};

    /// Policy for multiple arguments when `expected_ == 1`  (can be set on bool flags, too)
    MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw};

    /// Copy the contents to another similar class (one based on OptionBase)
    template <typename T> void copy_to(T *other) const {
        other->group(group_);
        other->required(required_);
        other->ignore_case(ignore_case_);
        other->ignore_underscore(ignore_underscore_);
        other->configurable(configurable_);
        other->disable_flag_override(disable_flag_override_);
        other->delimiter(delimiter_);
        other->always_capture_default(always_capture_default_);
        other->multi_option_policy(multi_option_policy_);
    }

  public:
    // setters

    /// Changes the group membership
    CRTP *group(std::string name) {
        group_ = name;
        return static_cast<CRTP *>(this);
    }

    /// Set the option as required
    CRTP *required(bool value = true) {
        required_ = value;
        return static_cast<CRTP *>(this);
    }

    /// Support Plumbum term
    CRTP *mandatory(bool value = true) { return required(value); }

    CRTP *always_capture_default(bool value = true) {
        always_capture_default_ = value;
        return static_cast<CRTP *>(this);
    }

    // Getters

    /// Get the group of this option
    const std::string &get_group() const { return group_; }

    /// True if this is a required option
    bool get_required() const { return required_; }

    /// The status of ignore case
    bool get_ignore_case() const { return ignore_case_; }

    /// The status of ignore_underscore
    bool get_ignore_underscore() const { return ignore_underscore_; }

    /// The status of configurable
    bool get_configurable() const { return configurable_; }

    /// The status of configurable
    bool get_disable_flag_override() const { return disable_flag_override_; }

    /// Get the current delimeter char
    char get_delimiter() const { return delimiter_; }

    /// Return true if this will automatically capture the default value for help printing
    bool get_always_capture_default() const { return always_capture_default_; }

    /// The status of the multi option policy
    MultiOptionPolicy get_multi_option_policy() const { return multi_option_policy_; }

    // Shortcuts for multi option policy

    /// Set the multi option policy to take last
    CRTP *take_last() {
        auto self = static_cast<CRTP *>(this);
        self->multi_option_policy(MultiOptionPolicy::TakeLast);
        return self;
    }

    /// Set the multi option policy to take last
    CRTP *take_first() {
        auto self = static_cast<CRTP *>(this);
        self->multi_option_policy(MultiOptionPolicy::TakeFirst);
        return self;
    }

    /// Set the multi option policy to take last
    CRTP *join() {
        auto self = static_cast<CRTP *>(this);
        self->multi_option_policy(MultiOptionPolicy::Join);
        return self;
    }

    /// Allow in a configuration file
    CRTP *configurable(bool value = true) {
        configurable_ = value;
        return static_cast<CRTP *>(this);
    }

    /// Allow in a configuration file
    CRTP *delimiter(char value = '\0') {
        delimiter_ = value;
        return static_cast<CRTP *>(this);
    }
};

/// This is a version of OptionBase that only supports setting values,
/// for defaults. It is stored as the default option in an App.
class OptionDefaults : public OptionBase<OptionDefaults> {
  public:
    OptionDefaults() = default;

    // Methods here need a different implementation if they are Option vs. OptionDefault

    /// Take the last argument if given multiple times
    OptionDefaults *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
        multi_option_policy_ = value;
        return this;
    }

    /// Ignore the case of the option name
    OptionDefaults *ignore_case(bool value = true) {
        ignore_case_ = value;
        return this;
    }

    /// Ignore underscores in the option name
    OptionDefaults *ignore_underscore(bool value = true) {
        ignore_underscore_ = value;
        return this;
    }

    /// Disable overriding flag values with an '=<value>' segment
    OptionDefaults *disable_flag_override(bool value = true) {
        disable_flag_override_ = value;
        return this;
    }

    /// set a delimiter character to split up single arguments to treat as multiple inputs
    OptionDefaults *delimiter(char value = '\0') {
        delimiter_ = value;
        return this;
    }
};

class Option : public OptionBase<Option> {
    friend App;

  protected:
    /// @name Names
    ///@{

    /// A list of the short names (`-a`) without the leading dashes
    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 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_;

    /// a list of flag names with specified default values;
    std::vector<std::string> fnames_;

    /// A positional name
    std::string pname_;

    /// If given, check the environment for this option
    std::string envname_;

    ///@}
    /// @name Help
    ///@{

    /// The description for help strings
    std::string description_;

    /// A human readable default value, either manually set, captured, or captured by default
    std::string default_str_;

    /// A human readable type value, set when App creates this
    ///
    /// This is a lambda function so "types" can be dynamic, such as when a set prints its contents.
    std::function<std::string()> type_name_{[]() { return std::string(); }};

    /// Run this function to capture a default (ignore if empty)
    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 expected values, type_size_ must be < 0. Ignored for flag. N < 0 means at least -N values.
    int expected_{1};

    /// 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_;

    /// A list of options that are excluded with this option
    std::set<Option *> excludes_;

    ///@}
    /// @name Other
    ///@{

    /// Remember the parent app
    App *parent_;

    /// Options store a callback to do all the work
    callback_t callback_;

    ///@}
    /// @name Parsing results
    ///@{

    /// Results of parsing
    results_t results_;

    /// Whether the callback has run (needed for INI parsing)
    bool callback_run_{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)
        : description_(std::move(option_description)), parent_(parent), callback_(std::move(callback)) {
        std::tie(snames_, lnames_, pname_) = detail::get_names(detail::split_names(option_name));
    }

  public:
    /// @name Basic
    ///@{

    /// Count the total number of times an option was passed
    size_t count() const { return results_.size(); }

    /// True if the option was not passed
    size_t empty() const { return results_.empty(); }

    /// This class is true if option is passed.
    operator bool() const { return !empty(); }

    /// Clear the parsed results (mostly for testing)
    void clear() { results_.clear(); }

    ///@}
    /// @name Setting options
    ///@{

    /// Set the number of expected arguments (Flags don't use this)
    Option *expected(int value) {

        // Break if this is a flag
        if(type_size_ == 0)
            throw IncorrectConstruction::SetFlag(get_name(true, true));

        // Setting 0 is not allowed
        else if(value == 0)
            throw IncorrectConstruction::Set0Opt(get_name());

        // No change is okay, quit now
        else if(expected_ == value)
            return this;

        // Type must be a vector
        else if(type_size_ >= 0)
            throw IncorrectConstruction::ChangeNotVector(get_name());

        // TODO: Can support multioption for non-1 values (except for join)
        else if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw)
            throw IncorrectConstruction::AfterMultiOpt(get_name());

        expected_ = value;
        return this;
    }

    /// Adds a Validator with a built in type name
    Option *check(Validator validator, std::string validator_name = "") {
        validator.non_modifying();
        validators_.push_back(std::move(validator));
        if(!validator_name.empty())
            validators_.front().name(validator_name);
        return this;
    }

    /// 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));
        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);
        return this;
    }

    /// Adds a validator-like function that can change result
    Option *transform(std::function<std::string(std::string)> func,
                      std::string transform_description = "",
                      std::string transform_name = "") {
        validators_.insert(validators_.begin(),
                           Validator(
                               [func](std::string &val) {
                                   val = func(val);
                                   return std::string{};
                               },
                               std::move(transform_description),
                               std::move(transform_name)));

        return this;
    }

    /// Adds a user supplied function to run on each item passed in (communicate though lambda capture)
    Option *each(std::function<void(std::string)> func) {
        validators_.emplace_back(
            [func](std::string &inout) {
                func(inout);
                return std::string{};
            },
            std::string{});
        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;
            }
        }
        if((validator_name.empty()) && (!validators_.empty())) {
            return &(validators_.front());
        }
        throw OptionNotFound(std::string("Validator ") + validator_name + " Not Found");
    }
    /// Sets required options
    Option *needs(Option *opt) {
        auto tup = needs_.insert(opt);
        if(!tup.second)
            throw OptionAlreadyAdded::Requires(get_name(), opt->get_name());
        return this;
    }

    /// Can find a string if needed
    template <typename T = App> Option *needs(std::string opt_name) {
        for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
            if(opt.get() != this && opt->check_name(opt_name))
                return needs(opt.get());
        throw IncorrectConstruction::MissingOption(opt_name);
    }

    /// Any number supported, any mix of string and Opt
    template <typename A, typename B, typename... ARG> Option *needs(A opt, B opt1, ARG... args) {
        needs(opt);
        return needs(opt1, args...);
    }

    /// Remove needs link from an option. Returns true if the option really was in the needs list.
    bool remove_needs(Option *opt) {
        auto iterator = std::find(std::begin(needs_), std::end(needs_), opt);

        if(iterator != std::end(needs_)) {
            needs_.erase(iterator);
            return true;
        } else {
            return false;
        }
    }

    /// Sets excluded options
    Option *excludes(Option *opt) {
        excludes_.insert(opt);

        // Help text should be symmetric - excluding a should exclude b
        opt->excludes_.insert(this);

        // Ignoring the insert return value, excluding twice is now allowed.
        // (Mostly to allow both directions to be excluded by user, even though the library does it for you.)

        return this;
    }

    /// Can find a string if needed
    template <typename T = App> Option *excludes(std::string opt_name) {
        for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
            if(opt.get() != this && opt->check_name(opt_name))
                return excludes(opt.get());
        throw IncorrectConstruction::MissingOption(opt_name);
    }

    /// Any number supported, any mix of string and Opt
    template <typename A, typename B, typename... ARG> Option *excludes(A opt, B opt1, ARG... args) {
        excludes(opt);
        return excludes(opt1, args...);
    }

    /// Remove needs link from an option. Returns true if the option really was in the needs list.
    bool remove_excludes(Option *opt) {
        auto iterator = std::find(std::begin(excludes_), std::end(excludes_), opt);

        if(iterator != std::end(excludes_)) {
            excludes_.erase(iterator);
            return true;
        } else {
            return false;
        }
    }

    /// Sets environment variable to read if no option given
    Option *envname(std::string name) {
        envname_ = name;
        return this;
    }

    /// Ignore case
    ///
    /// The template hides the fact that we don't have the definition of App yet.
    /// You are never expected to add an argument to the template here.
    template <typename T = App> Option *ignore_case(bool value = true) {
        ignore_case_ = value;
        auto *parent = dynamic_cast<T *>(parent_);

        for(const Option_p &opt : parent->options_)
            if(opt.get() != this && *opt == *this)
                throw OptionAlreadyAdded(opt->get_name(true, true));

        return this;
    }

    /// Ignore underscores in the option names
    ///
    /// The template hides the fact that we don't have the definition of App yet.
    /// You are never expected to add an argument to the template here.
    template <typename T = App> Option *ignore_underscore(bool value = true) {
        ignore_underscore_ = value;
        auto *parent = dynamic_cast<T *>(parent_);
        for(const Option_p &opt : parent->options_)
            if(opt.get() != this && *opt == *this)
                throw OptionAlreadyAdded(opt->get_name(true, true));

        return this;
    }

    /// 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;
        return this;
    }

    /// disable flag overrides
    Option *disable_flag_override(bool value = true) {
        disable_flag_override_ = value;
        return this;
    }
    ///@}
    /// @name Accessors
    ///@{

    /// The number of arguments the option expects
    int get_type_size() const { return type_size_; }

    /// The environment variable associated to this value
    std::string get_envname() const { return envname_; }

    /// The set of options needed
    std::set<Option *> get_needs() const { return needs_; }

    /// 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_; }

    /// Get the callback function
    callback_t get_callback() const { return callback_; }

    /// Get the long names
    const std::vector<std::string> get_lnames() const { return lnames_; }

    /// Get the short names
    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_; }

    /// The number of times the option expects to be included
    int get_expected() const { return expected_; }

    /// \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));
    }

    /// True if the argument can be given directly
    bool get_positional() const { return pname_.length() > 0; }

    /// True if option has at least one non-positional name
    bool nonpositional() const { return (snames_.size() + lnames_.size()) > 0; }

    /// True if option has description
    bool has_description() const { return description_.length() > 0; }

    /// Get the description
    const std::string &get_description() const { return description_; }

    /// Set the description
    Option *description(std::string option_description) {
        description_ = std::move(option_description);
        return this;
    }

    ///@}
    /// @name Help tools
    ///@{

    /// \brief Gets a comma separated list of names.
    /// Will include / prefer the positional name if positional is true.
    /// If all_options is false, pick just the most descriptive name to show.
    /// Use `get_name(true)` to get the positional name (replaces `get_pname`)
    std::string get_name(bool positional = false, //<[input] Show the positional name
                         bool all_options = false //<[input] Show every option
                         ) const {

        if(all_options) {

            std::vector<std::string> name_list;

            /// The all list will never include a positional unless asked or that's the only name.
            if((positional && pname_.length()) || (snames_.empty() && lnames_.empty()))
                name_list.push_back(pname_);
            if((get_items_expected() == 0) && (!fnames_.empty())) {
                for(const std::string &sname : snames_) {
                    name_list.push_back("-" + sname);
                    if(check_fname(sname)) {
                        name_list.back() += "{" + get_flag_value(sname, "") + "}";
                    }
                }

                for(const std::string &lname : lnames_) {
                    name_list.push_back("--" + lname);
                    if(check_fname(lname)) {
                        name_list.back() += "{" + get_flag_value(lname, "") + "}";
                    }
                }
            } else {
                for(const std::string &sname : snames_)
                    name_list.push_back("-" + sname);

                for(const std::string &lname : lnames_)
                    name_list.push_back("--" + lname);
            }

            return detail::join(name_list);

        } else {

            // This returns the positional name no matter what
            if(positional)
                return pname_;

            // Prefer long name
            else if(!lnames_.empty())
                return std::string("--") + lnames_[0];

            // Or short name if no long name
            else if(!snames_.empty())
                return std::string("-") + snames_[0];

            // If positional is the only name, it's okay to use that
            else
                return pname_;
        }
    }

    ///@}
    /// @name Parser tools
    ///@{

    /// Process the callback
    void run_callback() {

        callback_run_ = true;

        // Run the validators (can change the string)
        if(!validators_.empty()) {
            for(std::string &result : results_) {
                auto err_msg = _validate(result);
                if(!err_msg.empty())
                    throw ValidationError(get_name(), err_msg);
            }
        }
        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(local_result)
            throw ConversionError(get_name(), results_);
    }

    /// If options share any of the same names, they are equal (not counting positional)
    bool operator==(const Option &other) const {
        for(const std::string &sname : snames_)
            if(other.check_sname(sname))
                return true;
        for(const std::string &lname : lnames_)
            if(other.check_lname(lname))
                return true;

        if(ignore_case_ ||
           ignore_underscore_) { // We need to do the inverse, in case we are ignore_case or ignore underscore
            for(const std::string &sname : other.snames_)
                if(check_sname(sname))
                    return true;
            for(const std::string &lname : other.lnames_)
                if(check_lname(lname))
                    return true;
        }
        return false;
    }

    /// Check a name. Requires "-" or "--" for short / long, supports positional name
    bool check_name(std::string name) const {

        if(name.length() > 2 && name[0] == '-' && name[1] == '-')
            return check_lname(name.substr(2));
        else if(name.length() > 1 && name.front() == '-')
            return check_sname(name.substr(1));
        else {
            std::string local_pname = pname_;
            if(ignore_underscore_) {
                local_pname = detail::remove_underscore(local_pname);
                name = detail::remove_underscore(name);
            }
            if(ignore_case_) {
                local_pname = detail::to_lower(local_pname);
                name = detail::to_lower(name);
            }
            return name == local_pname;
        }
    }

    /// Requires "-" to be removed from string
    bool check_sname(std::string name) const { return (detail::find_member(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);
    }

    /// Requires "--" to be removed from string
    bool check_fname(std::string name) const {
        if(fnames_.empty()) {
            return false;
        }
        return (detail::find_member(name, fnames_, ignore_case_, ignore_underscore_) >= 0);
    }

    std::string get_flag_value(std::string name, std::string input_value) const {
        static const std::string trueString{"true"};
        static const std::string falseString{"false"};
        static const std::string emptyString{"{}"};
        // check for disable flag override_
        if(disable_flag_override_) {
            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) {
                        throw(ArgumentMismatch::FlagOverride(name));
                    }
                } else {
                    if(input_value != trueString) {
                        throw(ArgumentMismatch::FlagOverride(name));
                    }
                }
            }
        }
        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(ind < 0) {
            return input_value;
        }
        if(default_flag_values_[static_cast<size_t>(ind)].second == falseString) {
            try {
                auto val = detail::to_flag_value(input_value);
                return (val == 1) ? falseString : (val == (-1) ? trueString : std::to_string(-val));
            } catch(const std::invalid_argument &) {
                return input_value;
            }
        } else {
            return input_value;
        }
    }

    /// Puts a result at the end
    Option *add_result(std::string s) {
        _add_result(std::move(s));
        callback_run_ = false;
        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;
        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));
        }
        callback_run_ = false;
        return this;
    }

    /// Get a copy of the results
    std::vector<std::string> results() const { return results_; }

    /// get the results as a particular type
    template <typename T,
              enable_if_t<!is_vector<T>::value && !std::is_const<T>::value, detail::enabler> = detail::dummy>
    void results(T &output) const {
        bool retval;
        if(results_.empty()) {
            retval = detail::lexical_cast(default_str_, output);
        } else if(results_.size() == 1) {
            retval = detail::lexical_cast(results_[0], 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;
            }
        }
        if(!retval) {
            throw ConversionError(get_name(), results_);
        }
    }
    /// get the results as a vector of a particular 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 a particular type
    template <typename T> T as() const {
        T output;
        results(output);
        return output;
    }

    /// See if the callback has been run already
    bool get_callback_run() const { return callback_run_; }

    ///@}
    /// @name Custom options
    ///@{

    /// Set the type function to run when displayed on this option
    Option *type_name_fn(std::function<std::string()> typefun) {
        type_name_ = typefun;
        return this;
    }

    /// Set a custom option typestring
    Option *type_name(std::string typeval) {
        type_name_fn([typeval]() { return typeval; });
        return this;
    }

    /// Set a custom option size
    Option *type_size(int option_type_size) {
        type_size_ = option_type_size;
        if(type_size_ == 0)
            required_ = false;
        if(option_type_size < 0)
            expected_ = -1;
        return this;
    }

    /// Set a capture function for the default. Mostly used by App.
    Option *default_function(const std::function<std::string()> &func) {
        default_function_ = func;
        return this;
    }

    /// Capture the default value from the original value (if it can be captured)
    Option *capture_default_str() {
        if(default_function_) {
            default_str_ = default_function_();
        }
        return this;
    }

    /// Set the default value string representation (does not change the contained value)
    Option *default_str(std::string val) {
        default_str_ = 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();
        results_ = std::move(old_results);
        return this;
    }

    /// Get the full typename for this 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();
                if(!vtype.empty()) {
                    full_type_name += ":" + vtype;
                }
            }
        }
        return full_type_name;
    }

  private:
    // run through the validators
    std::string _validate(std::string &result) {
        std::string err_msg;
        for(const auto &vali : validators_) {
            try {
                err_msg = vali(result);
            } catch(const ValidationError &err) {
                err_msg = err.what();
            }
            if(!err_msg.empty())
                break;
        }
        return err_msg;
    }

    int _add_result(std::string &&result) {
        int result_count = 0;
        if(delimiter_ == '\0') {
            results_.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);
                        ++result_count;
                    }
                }
            } else {
                results_.push_back(std::move(result));
                ++result_count;
            }
        }
        return result_count;
    }
};

} // namespace CLI

// From CLI/App.hpp:

namespace CLI {

#ifndef CLI11_PARSE
#define CLI11_PARSE(app, argc, argv)                                                                                   \
    try {                                                                                                              \
        (app).parse((argc), (argv));                                                                                   \
    } catch(const CLI::ParseError &e) {                                                                                \
        return (app).exit(e);                                                                                          \
    }
#endif

namespace detail {
enum class Classifier { NONE, POSITIONAL_MARK, SHORT, LONG, WINDOWS, SUBCOMMAND, SUBCOMMAND_TERMINATOR };
struct AppFriend;
} // namespace detail

namespace FailureMessage {
std::string simple(const App *app, const Error &e);
std::string help(const App *app, const Error &e);
} // namespace FailureMessage

class App;

using App_p = std::shared_ptr<App>;

class Option_group;
/// Creates a command line program, with very few defaults.
/** To use, create a new `Program()` instance with `argc`, `argv`, and a help description. The templated
 *  add_option methods make it easy to prepare options. Remember to call `.start` before starting your
 * program, so that the options can be evaluated and the help option doesn't accidentally run your program. */
class App {
    friend Option;
    friend detail::AppFriend;

  protected:
    // This library follows the Google style guide for member names ending in underscores

    /// @name Basics
    ///@{

    /// Subcommand name or program name (from parser if name is empty)
    std::string name_;

    /// Description of the current program/subcommand
    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 true, return immediately on an unrecognized option (implies allow_extras) INHERITABLE
    bool prefix_command_{false};

    /// If set to true the name was automatically generated from the command line vs a user set name
    bool has_automatic_name_{false};

    /// If set to true the subcommand is required to be processed and used, ignored for main app
    bool required_{false};

    /// If set to true the subcommand is disabled and cannot be used, ignored for main app
    bool disabled_{false};

    /// Flag indicating that the pre_parse_callback has been triggered
    bool pre_parse_called_{false};

    /// Flag indicating that the callback for the subcommand should be executed immediately on parse completion which is
    /// before help or ini files are processed. INHERITABLE
    bool immediate_callback_{false};

    /// This is a function that runs prior to the start of parsing
    std::function<void(size_t)> pre_parse_callback_;

    /// This is a function that runs when complete. Great for subcommands. Can throw.
    std::function<void()> callback_;

    ///@}
    /// @name Options
    ///@{

    /// The default values for options, customizable and changeable INHERITABLE
    OptionDefaults option_defaults_;

    /// The list of options, stored locally
    std::vector<Option_p> options_;

    ///@}
    /// @name Help
    ///@{

    /// Footer to put after all options in the help output INHERITABLE
    std::string footer_;

    /// A pointer to the help flag if there is one INHERITABLE
    Option *help_ptr_{nullptr};

    /// A pointer to the help all flag if there is one INHERITABLE
    Option *help_all_ptr_{nullptr};

    /// This is the formatter for help printing. Default provided. INHERITABLE (same pointer)
    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;

    ///@}
    /// @name Parsing
    ///@{

    using missing_t = std::vector<std::pair<detail::Classifier, std::string>>;

    /// 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_;

    /// This is a list of pointers to options with the original parse order
    std::vector<Option *> parse_order_;

    /// This is a list of the subcommands collected, in order
    std::vector<App *> parsed_subcommands_;

    /// this is a list of subcommands that are exclusionary to this one
    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_;

    ///@}
    /// @name Subcommands
    ///@{

    /// Storage for subcommand list
    std::vector<App_p> subcommands_;

    /// If true, the program name is not case sensitive INHERITABLE
    bool ignore_case_{false};

    /// If true, the program should ignore underscores INHERITABLE
    bool ignore_underscore_{false};

    /// Allow subcommand fallthrough, so that parent commands can collect commands after subcommand.  INHERITABLE
    bool fallthrough_{false};

    /// Allow '/' for options for Windows like options. Defaults to true on Windows, false otherwise. INHERITABLE
    bool allow_windows_style_options_{
#ifdef _WIN32
        true
#else
        false
#endif
    };
    /// 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};
    /// 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;

    /// Minimum required subcommands (not inheritable!)
    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;

    /// Minimum required options (not inheritable!)
    size_t require_option_min_ = 0;

    /// Max number of options allowed. 0 is unlimited (not inheritable)
    size_t require_option_max_ = 0;

    /// The group membership INHERITABLE
    std::string group_{"Subcommands"};

    ///@}
    /// @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};

    /// This is the formatter for help printing. Default provided. INHERITABLE (same pointer)
    std::shared_ptr<Config> config_formatter_{new ConfigINI()};

    ///@}

    /// Special private constructor for subcommand
    App(std::string app_description, std::string app_name, App *parent)
        : name_(std::move(app_name)), description_(std::move(app_description)), parent_(parent) {
        // Inherit if not from a nullptr
        if(parent_ != nullptr) {
            if(parent_->help_ptr_ != nullptr)
                set_help_flag(parent_->help_ptr_->get_name(false, true), parent_->help_ptr_->get_description());
            if(parent_->help_all_ptr_ != nullptr)
                set_help_all_flag(parent_->help_all_ptr_->get_name(false, true),
                                  parent_->help_all_ptr_->get_description());

            /// OptionDefaults
            option_defaults_ = parent_->option_defaults_;

            // INHERITABLE
            failure_message_ = parent_->failure_message_;
            allow_extras_ = parent_->allow_extras_;
            allow_config_extras_ = parent_->allow_config_extras_;
            prefix_command_ = parent_->prefix_command_;
            immediate_callback_ = parent_->immediate_callback_;
            ignore_case_ = parent_->ignore_case_;
            ignore_underscore_ = parent_->ignore_underscore_;
            fallthrough_ = parent_->fallthrough_;
            validate_positionals_ = parent_->validate_positionals_;
            allow_windows_style_options_ = parent_->allow_windows_style_options_;
            group_ = parent_->group_;
            footer_ = parent_->footer_;
            formatter_ = parent_->formatter_;
            config_formatter_ = parent_->config_formatter_;
            require_subcommand_max_ = parent_->require_subcommand_max_;
        }
    }

  public:
    /// @name Basic
    ///@{

    /// Create a new program. Pass in the same arguments as main(), along with a help string.
    explicit App(std::string app_description = "", std::string app_name = "")
        : App(app_description, app_name, nullptr) {
        set_help_flag("-h,--help", "Print this help message and exit");
    }

    /// virtual destructor
    virtual ~App() = default;

    /// Set a callback for the end of parsing.
    ///
    /// Due to a bug in c++11,
    /// it is not possible to overload on std::function (fixed in c++14
    /// and backported to c++11 on newer compilers). Use capture by reference
    /// to get a pointer to App if needed.
    App *callback(std::function<void()> app_callback) {
        callback_ = std::move(app_callback);
        return this;
    }

    /// Set a callback to execute prior to parsing.
    ///
    App *preparse_callback(std::function<void(size_t)> pp_callback) {
        pre_parse_callback_ = std::move(pp_callback);
        return this;
    }

    /// Set a name for the app (empty will use parser to set the name)
    App *name(std::string app_name = "") {
        name_ = app_name;
        has_automatic_name_ = false;
        return this;
    }

    /// Remove the error when extras are left over on the command line.
    App *allow_extras(bool allow = true) {
        allow_extras_ = allow;
        return this;
    }

    /// Remove the error when extras are left over on the command line.
    App *required(bool require = true) {
        required_ = require;
        return this;
    }

    /// Disable the subcommand or option group
    App *disabled(bool disable = true) {
        disabled_ = disable;
        return this;
    }

    /// 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;
        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;
        return this;
    }

    /// Set the subcommand callback to be executed immediately on subcommand completion
    App *immediate_callback(bool immediate = true) {
        immediate_callback_ = immediate;
        return this;
    }

    /// Set the subcommand to validate positional arguments before assigning
    App *validate_positionals(bool validate = true) {
        validate_positionals_ = validate;
        return this;
    }

    /// Remove the error when extras are left over on the command line.
    /// Will also call App::allow_extras().
    App *allow_config_extras(bool allow = true) {
        allow_extras(allow);
        allow_config_extras_ = allow;
        return this;
    }

    /// Do not parse anything after the first unrecognized option and return
    App *prefix_command(bool allow = true) {
        prefix_command_ = allow;
        return this;
    }

    /// Ignore case. Subcommands inherit value.
    App *ignore_case(bool value = true) {
        ignore_case_ = value;
        if(parent_ != nullptr && !name_.empty()) {
            for(const auto &subc : parent_->subcommands_) {
                if(subc.get() != this && (this->check_name(subc->name_) || subc->check_name(this->name_)))
                    throw OptionAlreadyAdded(subc->name_);
            }
        }
        return this;
    }

    /// Allow windows style options, such as `/opt`. First matching short or long name used. Subcommands inherit value.
    App *allow_windows_style_options(bool value = true) {
        allow_windows_style_options_ = value;
        return this;
    }

    /// Specify that the positional arguments are only at the end of the sequence
    App *positionals_at_end(bool value = true) {
        positionals_at_end_ = value;
        return this;
    }

    /// Ignore underscore. Subcommands inherit value.
    App *ignore_underscore(bool value = true) {
        ignore_underscore_ = value;
        if(parent_ != nullptr && !name_.empty()) {
            for(const auto &subc : parent_->subcommands_) {
                if(subc.get() != this && (this->check_name(subc->name_) || subc->check_name(this->name_)))
                    throw OptionAlreadyAdded(subc->name_);
            }
        }
        return this;
    }

    /// Set the help formatter
    App *formatter(std::shared_ptr<FormatterBase> fmt) {
        formatter_ = fmt;
        return this;
    }

    /// Set the help formatter
    App *formatter_fn(std::function<std::string(const App *, std::string, AppFormatMode)> fmt) {
        formatter_ = std::make_shared<FormatterLambda>(fmt);
        return this;
    }

    /// Set the config formatter
    App *config_formatter(std::shared_ptr<Config> fmt) {
        config_formatter_ = fmt;
        return this;
    }

    /// Check to see if this subcommand was parsed, true only if received on command line.
    bool parsed() const { return parsed_ > 0; }

    /// Get the OptionDefault object, to set option defaults
    OptionDefaults *option_defaults() { return &option_defaults_; }

    ///@}
    /// @name Adding options
    ///@{

    /// Add an option, will automatically understand the type for common types.
    ///
    /// To use, create a variable with the expected type, and pass it in after the name.
    /// After start is called, you can use count to see if the value was passed, and
    /// the value will be initialized properly. Numbers, vectors, and strings are supported.
    ///
    /// ->required(), ->default, and the validators are options,
    /// The positional options take an optional number of arguments.
    ///
    /// For example,
    ///
    ///     std::string filename;
    ///     program.add_option("filename", filename, "description of filename");
    ///
    Option *add_option(std::string option_name,
                       callback_t option_callback,
                       std::string option_description = "",
                       bool defaulted = false,
                       std::function<std::string()> func = {}) {
        Option myopt{option_name, option_description, option_callback, this};

        if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) {
               return *v == myopt;
           }) == std::end(options_)) {
            options_.emplace_back();
            Option_p &option = options_.back();
            option.reset(new Option(option_name, option_description, option_callback, this));

            // Set the default string capture function
            option->default_function(func);

            // For compatibility with CLI11 1.7 and before, capture the default string here
            if(defaulted)
                option->capture_default_str();

            // Transfer defaults to the new option
            option_defaults_.copy_to(option.get());

            // Don't bother to capture if we already did
            if(!defaulted && option->get_always_capture_default())
                option->capture_default_str();

            return option.get();

        } else
            throw OptionAlreadyAdded(myopt.get_name());
    }

    /// Add option for non-vectors (duplicate copy needed without defaulted to avoid `iostream << value`)
    template <typename T, enable_if_t<!is_vector<T>::value & !std::is_const<T>::value, detail::enabler> = detail::dummy>
    Option *add_option(std::string option_name,
                       T &variable, ///< The variable to set
                       std::string option_description = "",
                       bool defaulted = false) {

        auto fun = [&variable](CLI::results_t res) { return detail::lexical_cast(res[0], variable); };

        Option *opt = add_option(option_name, fun, option_description, defaulted, [&variable]() {
            return std::string(CLI::detail::to_string(variable));
        });
        opt->type_name(detail::type_name<T>());

        return opt;
    }

    /// Add option for a callback of a specific type
    template <typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
    Option *add_option_function(std::string option_name,
                                const std::function<void(const T &)> &func, ///< the callback to execute
                                std::string option_description = "") {

        auto fun = [func](CLI::results_t res) {
            T variable;
            bool result = detail::lexical_cast(res[0], variable);
            if(result) {
                func(variable);
            }
            return result;
        };

        Option *opt = add_option(option_name, std::move(fun), option_description, false);
        opt->type_name(detail::type_name<T>());
        return opt;
    }

    /// Add option with no description or variable assignment
    Option *add_option(std::string option_name) {
        return add_option(option_name, CLI::callback_t(), std::string{}, false);
    }

    /// Add option with description but with no variable assignment or callback
    template <typename T,
              enable_if_t<std::is_const<T>::value && std::is_constructible<std::string, T>::value, detail::enabler> =
                  detail::dummy>
    Option *add_option(std::string option_name, T &option_description) {
        return add_option(option_name, CLI::callback_t(), option_description, false);
    }

    /// Add option for vectors
    template <typename T>
    Option *add_option(std::string option_name,
                       std::vector<T> &variable, ///< The variable vector to set
                       std::string option_description = "",
                       bool defaulted = false) {

        auto fun = [&variable](CLI::results_t res) {
            bool retval = true;
            variable.clear();
            variable.reserve(res.size());
            for(const auto &elem : res) {

                variable.emplace_back();
                retval &= detail::lexical_cast(elem, variable.back());
            }
            return (!variable.empty()) && retval;
        };

        auto default_function = [&variable]() {
            std::vector<std::string> defaults;
            defaults.resize(variable.size());
            std::transform(variable.begin(), variable.end(), defaults.begin(), [](T &val) {
                return std::string(CLI::detail::to_string(val));
            });
            return std::string("[" + detail::join(defaults) + "]");
        };

        Option *opt = add_option(option_name, fun, option_description, defaulted, default_function);
        opt->type_name(detail::type_name<T>())->type_size(-1);

        return opt;
    }

    /// Add option for a vector callback of a specific type
    template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
    Option *add_option_function(std::string option_name,
                                const std::function<void(const T &)> &func, ///< the callback to execute
                                std::string option_description = "") {

        CLI::callback_t fun = [func](CLI::results_t res) {
            T values;
            bool retval = true;
            values.reserve(res.size());
            for(const auto &elem : res) {
                values.emplace_back();
                retval &= detail::lexical_cast(elem, values.back());
            }
            if(retval) {
                func(values);
            }
            return retval;
        };

        Option *opt = add_option(option_name, std::move(fun), std::move(option_description), false);
        opt->type_name(detail::type_name<T>())->type_size(-1);
        return opt;
    }

    /// Set a help flag, replace the existing one if present
    Option *set_help_flag(std::string flag_name = "", const std::string &help_description = "") {
        // take flag_description by const reference otherwise add_flag tries to assign to help_description
        if(help_ptr_ != nullptr) {
            remove_option(help_ptr_);
            help_ptr_ = nullptr;
        }

        // Empty name will simply remove the help flag
        if(!flag_name.empty()) {
            help_ptr_ = add_flag(flag_name, help_description);
            help_ptr_->configurable(false);
        }

        return help_ptr_;
    }

    /// Set a help all flag, replaced the existing one if present
    Option *set_help_all_flag(std::string help_name = "", const std::string &help_description = "") {
        // take flag_description by const reference otherwise add_flag tries to assign to flag_description
        if(help_all_ptr_ != nullptr) {
            remove_option(help_all_ptr_);
            help_all_ptr_ = nullptr;
        }

        // Empty name will simply remove the help all flag
        if(!help_name.empty()) {
            help_all_ptr_ = add_flag(help_name, help_description);
            help_all_ptr_->configurable(false);
        }

        return help_all_ptr_;
    }

  private:
    /// Internal function for adding a flag
    Option *_add_flag_internal(std::string flag_name, CLI::callback_t fun, std::string flag_description) {
        Option *opt;
        if(detail::has_default_flag_values(flag_name)) {
            // check for default values and if it has them
            auto flag_defaults = detail::get_default_flag_values(flag_name);
            detail::remove_default_flag_values(flag_name);
            opt = add_option(std::move(flag_name), std::move(fun), std::move(flag_description), false);
            for(const auto &fname : flag_defaults)
                opt->fnames_.push_back(fname.first);
            opt->default_flag_values_ = std::move(flag_defaults);
        } else {
            opt = add_option(std::move(flag_name), std::move(fun), std::move(flag_description), false);
        }
        // flags cannot have positional values
        if(opt->get_positional()) {
            auto pos_name = opt->get_name(true);
            remove_option(opt);
            throw IncorrectConstruction::PositionalFlag(pos_name);
        }

        opt->type_size(0);
        return opt;
    }

  public:
    /// Add a flag with no description or variable assignment
    Option *add_flag(std::string flag_name) { return _add_flag_internal(flag_name, CLI::callback_t(), std::string{}); }

    /// Add flag with description but with no variable assignment or callback
    /// takes a constant string,  if a variable string is passed that variable will be assigned the results from the
    /// flag
    template <typename T,
              enable_if_t<std::is_const<T>::value && std::is_constructible<std::string, T>::value, detail::enabler> =
                  detail::dummy>
    Option *add_flag(std::string flag_name, T &flag_description) {
        return _add_flag_internal(flag_name, CLI::callback_t(), flag_description);
    }

    /// Add option for flag with integer result - defaults to allowing multiple passings, but can be forced to one if
    /// `multi_option_policy(CLI::MultiOptionPolicy::Throw)` is used.
    template <typename T,
              enable_if_t<std::is_integral<T>::value && !is_bool<T>::value, detail::enabler> = detail::dummy>
    Option *add_flag(std::string flag_name,
                     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) {
            try {
                detail::sum_flag_vector(res, flag_count);
            } catch(const std::invalid_argument &) {
                return false;
            }
            return true;
        };
        return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
    }

    /// Other type version accepts all other types that are not vectors such as bool, enum, string or other classes that
    /// can be converted from a string
    template <typename T,
              enable_if_t<!is_vector<T>::value && !std::is_const<T>::value &&
                              (!std::is_integral<T>::value || is_bool<T>::value) &&
                              !std::is_constructible<std::function<void(int)>, T>::value,
                          detail::enabler> = detail::dummy>
    Option *add_flag(std::string flag_name,
                     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;
            }
            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;
    }

    /// 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>
    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) {
            bool retval = true;
            for(const auto &elem : res) {
                flag_results.emplace_back();
                retval &= detail::lexical_cast(elem, flag_results.back());
            }
            return retval;
        };
        return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
    }

    /// Add option for callback that is triggered with a true flag and takes no arguments
    Option *add_flag_callback(std::string flag_name,
                              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;
            auto result = CLI::detail::lexical_cast(res[0], trigger);
            if(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;
    }

    /// 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::string flag_description = "") {

        CLI::callback_t fun = [function](CLI::results_t res) {
            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));
    }

#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::string flag_description = "") {
        return add_flag_function(std::move(flag_name), std::move(function), std::move(flag_description));
    }
#endif

    /// Add set of options (No default, temp reference, such as an inline set) DEPRECATED
    template <typename T>
    Option *add_set(std::string option_name,
                    T &member,           ///< The selected member of the set
                    std::set<T> options, ///< The set of possibilities
                    std::string option_description = "") {

        Option *opt = add_option(option_name, member, std::move(option_description));
        opt->check(IsMember{options});
        return opt;
    }

    /// Add set of options (No default, set can be changed afterwards - do not destroy the set) DEPRECATED
    template <typename T>
    Option *add_mutable_set(std::string option_name,
                            T &member,                  ///< The selected member of the set
                            const std::set<T> &options, ///< The set of possibilities
                            std::string option_description = "") {

        Option *opt = add_option(option_name, member, std::move(option_description));
        opt->check(IsMember{&options});
        return opt;
    }

    /// Add set of options (with default, static set, such as an inline set) DEPRECATED
    template <typename T>
    Option *add_set(std::string option_name,
                    T &member,           ///< The selected member of the set
                    std::set<T> options, ///< The set of possibilities
                    std::string option_description,
                    bool defaulted) {

        Option *opt = add_option(option_name, member, std::move(option_description), defaulted);
        opt->check(IsMember{options});
        return opt;
    }

    /// Add set of options (with default, set can be changed afterwards - do not destroy the set) DEPRECATED
    template <typename T>
    Option *add_mutable_set(std::string option_name,
                            T &member,                  ///< The selected member of the set
                            const std::set<T> &options, ///< The set of possibilities
                            std::string option_description,
                            bool defaulted) {

        Option *opt = add_option(option_name, member, std::move(option_description), defaulted);
        opt->check(IsMember{&options});
        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;
    }