diff --git a/thirdparty/CLI11.hpp b/thirdparty/CLI11.hpp index 41027f0e..8a5b4c54 100644 --- a/thirdparty/CLI11.hpp +++ b/thirdparty/CLI11.hpp @@ -1,11 +1,11 @@ -// CLI11: Version 2.4.1 +// CLI11: Version 2.4.2 // Originally designed by Henry Schreiner // https://github.com/CLIUtils/CLI11 // // This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts -// from: v2.4.1 +// from: v2.4.2 // -// CLI11 2.4.1 Copyright (c) 2017-2024 University of Cincinnati, developed by Henry +// CLI11 2.4.2 Copyright (c) 2017-2024 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 @@ -66,8 +66,8 @@ #define CLI11_VERSION_MAJOR 2 #define CLI11_VERSION_MINOR 4 -#define CLI11_VERSION_PATCH 1 -#define CLI11_VERSION "2.4.1" +#define CLI11_VERSION_PATCH 2 +#define CLI11_VERSION "2.4.2" @@ -1451,22 +1451,22 @@ class RequiredError : public ParseError { if((min_option == 1) && (max_option == 1) && (used == 0)) return RequiredError("Exactly 1 option from [" + option_list + "]"); if((min_option == 1) && (max_option == 1) && (used > 1)) { - return {"Exactly 1 option from [" + option_list + "] is required and " + std::to_string(used) + + return {"Exactly 1 option from [" + option_list + "] is required but " + std::to_string(used) + " were given", ExitCodes::RequiredError}; } if((min_option == 1) && (used == 0)) return RequiredError("At least 1 option from [" + option_list + "]"); if(used < min_option) { - return {"Requires at least " + std::to_string(min_option) + " options used and only " + - std::to_string(used) + "were given from [" + option_list + "]", + return {"Requires at least " + std::to_string(min_option) + " options used but only " + + std::to_string(used) + " were given from [" + option_list + "]", ExitCodes::RequiredError}; } if(max_option == 1) return {"Requires at most 1 options be given from [" + option_list + "]", ExitCodes::RequiredError}; - return {"Requires at most " + std::to_string(max_option) + " options be used and " + std::to_string(used) + - "were given from [" + option_list + "]", + return {"Requires at most " + std::to_string(max_option) + " options be used but " + std::to_string(used) + + " were given from [" + option_list + "]", ExitCodes::RequiredError}; } }; @@ -1633,6 +1633,23 @@ template <> struct IsMemberType { using type = std::string; }; +namespace adl_detail { +/// Check for existence of user-supplied lexical_cast. +/// +/// This struct has to be in a separate namespace so that it doesn't see our lexical_cast overloads in CLI::detail. +/// Standard says it shouldn't see them if it's defined before the corresponding lexical_cast declarations, but this +/// requires a working implementation of two-phase lookup, and not all compilers can boast that (msvc, ahem). +template class is_lexical_castable { + template + static auto test(int) -> decltype(lexical_cast(std::declval(), std::declval()), std::true_type()); + + template static auto test(...) -> std::false_type; + + public: + static constexpr bool value = decltype(test(0))::value; +}; +} // namespace adl_detail + namespace detail { // These are utilities for IsMember and other transforming objects @@ -1714,7 +1731,7 @@ template class is_direct_constructible { #pragma diag_suppress 2361 #endif #endif - TT{std::declval()} + TT{std::declval()} #ifdef __CUDACC__ #ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ #pragma nv_diag_default 2361 @@ -1722,8 +1739,8 @@ template class is_direct_constructible { #pragma diag_default 2361 #endif #endif - , - std::is_move_assignable()); + , + std::is_move_assignable()); template static auto test(int, std::false_type) -> std::false_type; @@ -2793,13 +2810,24 @@ bool lexical_cast(const std::string &input, T &output) { /// Non-string parsable by a stream template ::value == object_category::other && !std::is_assignable::value, + enable_if_t::value == object_category::other && !std::is_assignable::value && + is_istreamable::value, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - static_assert(is_istreamable::value, + return from_stream(input, output); +} + +/// Fallback overload that prints a human-readable error for types that we don't recognize and that don't have a +/// user-supplied lexical_cast overload. +template ::value == object_category::other && !std::is_assignable::value && + !is_istreamable::value && !adl_detail::is_lexical_castable::value, + detail::enabler> = detail::dummy> +bool lexical_cast(const std::string & /*input*/, T & /*output*/) { + static_assert(!std::is_same::value, // Can't just write false here. "option object type must have a lexical cast overload or streaming input operator(>>) defined, if it " "is convertible from another type use the add_option(...) with XC being the known type"); - return from_stream(input, output); + return false; } /// Assign a value through lexical cast operations @@ -7097,6 +7125,10 @@ class App { /// Check to see if a subcommand is part of this command (text version) CLI11_NODISCARD App *get_subcommand(std::string subcom) const; + /// Get a subcommand by name (noexcept non-const version) + /// returns null if subcommand doesn't exist + CLI11_NODISCARD App *get_subcommand_no_throw(std::string subcom) const noexcept; + /// Get a pointer to subcommand by index CLI11_NODISCARD App *get_subcommand(int index = 0) const; @@ -7271,8 +7303,9 @@ class App { } /// Check with name instead of pointer to see if subcommand was selected - CLI11_NODISCARD bool got_subcommand(std::string subcommand_name) const { - return get_subcommand(subcommand_name)->parsed_ > 0; + CLI11_NODISCARD bool got_subcommand(std::string subcommand_name) const noexcept { + App *sub = get_subcommand_no_throw(subcommand_name); + return (sub != nullptr) ? (sub->parsed_ > 0) : false; } /// Sets excluded options for the subcommand @@ -7402,7 +7435,7 @@ class App { std::vector