diff --git a/.github/workflows/update-catch.yaml b/.github/workflows/update-catch.yaml new file mode 100644 index 0000000000..a4f7de2c4d --- /dev/null +++ b/.github/workflows/update-catch.yaml @@ -0,0 +1,48 @@ +name: Update Catch +on: + workflow_dispatch: + schedule: + - cron: '0 10 1/16 * *' +jobs: + updateCatch: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Get Latest Catch Tagged Release + id: catch-header + run: | + # Ping version information upstream. + CATCH_RELEASE_JSON=$(curl -sL https://api.github.com/repos/catchorg/Catch2/releases/latest) + CATCH_RELEASE_VERSION=$(jq -r ".tag_name" <<< "$CATCH_RELEASE_JSON" | tr -d v) + echo ::set-output name=release_tag::$(echo $CATCH_RELEASE_VERSION) + # Extract out version information from git repository. + CATCH_VERSION_MAJOR=$(grep -i ".*#define CATCH_VERSION_MAJOR.*" src/mlpack/tests/catch.hpp | grep -o "[0-9]*") + CATCH_VERSION_MINOR=$(grep -i ".*#define CATCH_VERSION_MINOR.*" src/mlpack/tests/catch.hpp | grep -o "[0-9]*") + CATCH_VERSION_PATCH=$(grep -i ".*#define CATCH_VERSION_PATCH.*" src/mlpack/tests/catch.hpp | grep -o "[0-9]*") + # Combine values to match release tag information. + CATCH_VERSION_VALUE=${CATCH_VERSION_MAJOR}.${CATCH_VERSION_MINOR}.${CATCH_VERSION_PATCH} + # Set the current release tag. + echo ::set-output name=current_tag::$(echo $CATCH_VERSION_VALUE) + + - name: Update Catch + if: steps.catch-header.outputs.current_tag != steps.catch-header.outputs.release_tag + env: + CURRENT_TAG: ${{ steps.catch-header.outputs.current_tag }} + RELEASE_TAG: ${{ steps.catch-header.outputs.release_tag }} + run: | + # Delete the catch.hpp. + rm -f src/mlpack/tests/catch.hpp + # Download the release. + curl -sL https://github.com/catchorg/Catch2/releases/latest/download/catch.hpp -o src/mlpack/tests/catch.hpp + + - name: Create Pull Request For Catch + if: steps.catch-header.outputs.current_tag != steps.catch-header.outputs.release_tag + uses: peter-evans/create-pull-request@v3 + with: + commit-message: Upgrade Catch to ${{ steps.catch-header.outputs.release_tag }} + title: Upgrade Catch to ${{ steps.catch-header.outputs.release_tag }} + body: | + Updates [catchorg/Catch2](https://github.com/catchorg/Catch2) to ${{ steps.catch-header.outputs.release_tag }}. + Auto-generated by [create-pull-request](https://github.com/peter-evans/create-pull-request). + labels: update dependencies, automated PR + branch: catch-header-updates-${{ steps.catch-header.outputs.release_tag }} diff --git a/.github/workflows/update-cli11.yaml b/.github/workflows/update-cli11.yaml new file mode 100644 index 0000000000..e935183cdf --- /dev/null +++ b/.github/workflows/update-cli11.yaml @@ -0,0 +1,44 @@ +name: Update CLI11 +on: + workflow_dispatch: + schedule: + - cron: '0 10 1/16 * *' +jobs: + updateCLI11: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Get Latest CLI11 Tagged Release + id: cli11-header + run: | + # Ping version information upstream. + CLI11_RELEASE_JSON=$(curl -sL https://api.github.com/repos/CLIUtils/CLI11/releases/latest) + CLI11_RELEASE_VERSION=$(jq -r ".tag_name" <<< "$CLI11_RELEASE_JSON" | tr -d v) + echo ::set-output name=release_tag::$(echo $CLI11_RELEASE_VERSION) + # Extract out version information from git repository. + CLI11_VERSION_VALUE=$(grep -i ".*#define CLI11_VERSION.*" src/mlpack/bindings/cli/third_party/CLI/CLI11.hpp | grep -Po "(\d+\.)+\d+") + # Set the current release tag. + echo ::set-output name=current_tag::$(echo $CLI11_VERSION_VALUE) + + - name: Update CLI11 + if: steps.cli11-header.outputs.current_tag != steps.cli11-header.outputs.release_tag + env: + CURRENT_TAG: ${{ steps.cli11-header.outputs.current_tag }} + RELEASE_TAG: ${{ steps.cli11-header.outputs.release_tag }} + run: | + # Delete the CLI11.hpp. + rm -f src/mlpack/bindings/cli/third_party/CLI/CLI11.hpp + # Download the release. + curl -sL https://github.com/CLIUtils/CLI11/releases/latest/download/CLI11.hpp -o src/mlpack/bindings/cli/third_party/CLI/CLI11.hpp + + - name: Create Pull Request For CLI11 + if: steps.cli11-header.outputs.current_tag != steps.cli11-header.outputs.release_tag + uses: peter-evans/create-pull-request@v3 + with: + commit-message: Upgrade CLI11 to ${{ steps.cli11-header.outputs.release_tag }} + title: Upgrade CLI11 to ${{ steps.cli11-header.outputs.release_tag }} + body: | + Updates [CLIUtils/CLI11](https://github.com/CLIUtils/CLI11) to ${{ steps.cli11-header.outputs.release_tag }}. + Auto-generated by [create-pull-request](https://github.com/peter-evans/create-pull-request). + labels: update dependencies, automated PR + branch: cli11-header-updates-${{ steps.cli11-header.outputs.release_tag }} diff --git a/src/mlpack/bindings/cli/third_party/CLI/CLI11.hpp b/src/mlpack/bindings/cli/third_party/CLI/CLI11.hpp index 6674a32c2e..68244d3864 100644 --- a/src/mlpack/bindings/cli/third_party/CLI/CLI11.hpp +++ b/src/mlpack/bindings/cli/third_party/CLI/CLI11.hpp @@ -1,11 +1,11 @@ #pragma once -// CLI11: Version 1.9.0 +// CLI11: Version 1.9.1 // 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.9.0-49-ga2bf5cd +// from: v1.9.1 // // From LICENSE: // @@ -67,8 +67,8 @@ #define CLI11_VERSION_MAJOR 1 #define CLI11_VERSION_MINOR 9 -#define CLI11_VERSION_PATCH 0 -#define CLI11_VERSION "1.9.0" +#define CLI11_VERSION_PATCH 1 +#define CLI11_VERSION "1.9.1" @@ -334,12 +334,6 @@ inline bool valid_name_string(const std::string &str) { return true; } -/// check if a string is a container segment separator (empty or "%%") -inline bool is_separator(const std::string &str) { - static const std::string sep("%%"); - return (str.empty() || str == sep); -} - /// 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()); }); @@ -672,26 +666,19 @@ class Success : public ParseError { }; /// -h or --help on command line -class CallForHelp : public Success { - CLI11_ERROR_DEF(Success, CallForHelp) +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 Success { - CLI11_ERROR_DEF(Success, CallForAllHelp) +class CallForAllHelp : public ParseError { + CLI11_ERROR_DEF(ParseError, CallForAllHelp) CallForAllHelp() : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} }; -/// -v or --version on command line -class CallForVersion : public Success { - CLI11_ERROR_DEF(Success, CallForVersion) - CallForVersion() - : CallForVersion("This should be caught in your main function, see examples", ExitCodes::Success) {} -}; - -/// Does not output a diagnostic in CLI11_PARSE, but allows main() to return with a specific error code. +/// 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) {} @@ -893,6 +880,15 @@ template using void_t = typename make_void::type; /// A copy of std::conditional_t from C++14 - same reasoning as enable_if_t, it does not hurt to redefine template using conditional_t = typename std::conditional::type; +/// Check to see if something is a vector (fail check by default) +template struct is_vector : std::false_type {}; + +/// Check to see if something is a vector (true if actually a vector) +template struct is_vector> : std::true_type {}; + +/// Check to see if something is a vector (true if actually a const vector) +template struct is_vector> : std::true_type {}; + /// Check to see if something is bool (fail check by default) template struct is_bool : std::false_type {}; @@ -1034,17 +1030,6 @@ template class is_istreamable { static constexpr bool value = decltype(test(0))::value; }; -/// Check for complex -template class is_complex { - template - static auto test(int) -> decltype(std::declval().real(), std::declval().imag(), std::true_type()); - - template static auto test(...) -> std::false_type; - - public: - static constexpr bool value = decltype(test(0))::value; -}; - /// Templated operation to get a value from a stream template ::value, detail::enabler> = detail::dummy> bool from_stream(const std::string &istring, T &obj) { @@ -1059,49 +1044,12 @@ bool from_stream(const std::string & /*istring*/, T & /*obj*/) { return false; } -// check to see if an object is a mutable container (fail by default) -template struct is_mutable_container : std::false_type {}; - -/// type trait to test if a type is a mutable container meaning it has a value_type, it has an iterator, a clear, and -/// end methods and an insert function. And for our purposes we exclude std::string and types that can be constructed -/// from a std::string -template -struct is_mutable_container< - T, - conditional_t().end()), - decltype(std::declval().clear()), - decltype(std::declval().insert(std::declval().end())>(), - std::declval()))>, - void>> - : public conditional_t::value, std::false_type, std::true_type> {}; - -// check to see if an object is a mutable container (fail by default) -template struct is_readable_container : std::false_type {}; - -/// type trait to test if a type is a container meaning it has a value_type, it has an iterator, a clear, and an end -/// methods and an insert function. And for our purposes we exclude std::string and types that can be constructed from -/// a std::string -template -struct is_readable_container< - T, - conditional_t().end()), decltype(std::declval().begin())>, void>> - : public std::true_type {}; - -// check to see if an object is a wrapper (fail by default) -template struct is_wrapper : std::false_type {}; - -// check if an object is a wrapper (it has a value_type defined) -template -struct is_wrapper, void>> : public std::true_type {}; - // Check for tuple like types, as in classes with a tuple_size type trait template class is_tuple_like { template // static auto test(int) // -> decltype(std::conditional<(std::tuple_size::value > 0), std::true_type, std::false_type>::type()); - static auto test(int) -> decltype(std::tuple_size::type>::value, std::true_type{}); + static auto test(int) -> decltype(std::tuple_size::value, std::true_type{}); template static auto test(...) -> std::false_type; public: @@ -1136,19 +1084,20 @@ std::string to_string(T &&value) { /// If conversion is not supported, return an empty string (streaming is not supported for that type) template ::value && !is_ostreamable::value && - !is_readable_container::type>::value, + !is_vector::type>::type>::value, detail::enabler> = detail::dummy> std::string to_string(T &&) { return std::string{}; } -/// convert a readable container to a string +/// convert a vector to a string template ::value && !is_ostreamable::value && - is_readable_container::value, + is_vector::type>::type>::value, detail::enabler> = detail::dummy> std::string to_string(T &&variable) { std::vector defaults; + defaults.reserve(variable.size()); auto cval = variable.begin(); auto end = variable.end(); while(cval != end) { @@ -1192,142 +1141,25 @@ auto value_string(const T &value) -> decltype(to_string(value)) { return to_string(value); } -/// template to get the underlying value type if it exists or use a default -template struct wrapped_type { using type = def; }; - -/// Type size for regular object types that do not look like a tuple -template struct wrapped_type::value>::type> { - using type = typename T::value_type; -}; - -/// This will only trigger for actual void type -template struct type_count_base { static const int value{0}; }; - -/// Type size for regular object types that do not look like a tuple -template -struct type_count_base::value && !is_mutable_container::value && - !std::is_void::value>::type> { - static constexpr int value{1}; -}; - -/// the base tuple size -template -struct type_count_base::value && !is_mutable_container::value>::type> { - static constexpr int value{std::tuple_size::value}; -}; - -/// Type count base for containers is the type_count_base of the individual element -template struct type_count_base::value>::type> { - static constexpr int value{type_count_base::value}; -}; - -/// Set of overloads to get the type size of an object - -/// forward declare the subtype_count structure -template struct subtype_count; - -/// forward declare the subtype_count_min structure -template struct subtype_count_min; - /// This will only trigger for actual void type template struct type_count { static const int value{0}; }; -/// Type size for regular object types that do not look like a tuple -template -struct type_count::value && !is_tuple_like::value && !is_complex::value && - !std::is_void::value>::type> { - static constexpr int value{1}; -}; - -/// Type size for complex since it sometimes looks like a wrapper -template struct type_count::value>::type> { - static constexpr int value{2}; -}; - -/// Type size of types that are wrappers,except complex and tuples(which can also be wrappers sometimes) -template struct type_count::value>::type> { - static constexpr int value{subtype_count::value}; -}; - -/// Type size of types that are wrappers,except containers complex and tuples(which can also be wrappers sometimes) -template -struct type_count::value && !is_complex::value && !is_tuple_like::value && - !is_mutable_container::value>::type> { - static constexpr int value{type_count::value}; -}; - -/// 0 if the index > tuple size -template -constexpr typename std::enable_if::value, int>::type tuple_type_size() { - return 0; -} - -/// Recursively generate the tuple type name -template - constexpr typename std::enable_if < I::value, int>::type tuple_type_size() { - return subtype_count::type>::value + tuple_type_size(); -} - -/// Get the type size of the sum of type sizes for all the individual tuple types +/// Set of overloads to get the type size of an object template struct type_count::value>::type> { - static constexpr int value{tuple_type_size()}; + static constexpr int value{std::tuple_size::value}; }; - -/// definition of subtype count -template struct subtype_count { - static constexpr int value{is_mutable_container::value ? expected_max_vector_size : type_count::value}; -}; - -/// This will only trigger for actual void type -template struct type_count_min { static const int value{0}; }; - /// Type size for regular object types that do not look like a tuple template -struct type_count_min< +struct type_count< T, - typename std::enable_if::value && !is_tuple_like::value && !is_wrapper::value && - !is_complex::value && !std::is_void::value>::type> { - static constexpr int value{type_count::value}; -}; - -/// Type size for complex since it sometimes looks like a wrapper -template struct type_count_min::value>::type> { + typename std::enable_if::value && !is_tuple_like::value && !std::is_void::value>::type> { static constexpr int value{1}; }; -/// Type size min of types that are wrappers,except complex and tuples(which can also be wrappers sometimes) -template -struct type_count_min< - T, - typename std::enable_if::value && !is_complex::value && !is_tuple_like::value>::type> { - static constexpr int value{subtype_count_min::value}; -}; - -/// 0 if the index > tuple size -template -constexpr typename std::enable_if::value, int>::type tuple_type_size_min() { - return 0; -} - -/// Recursively generate the tuple type name -template - constexpr typename std::enable_if < I::value, int>::type tuple_type_size_min() { - return subtype_count_min::type>::value + tuple_type_size_min(); -} - -/// Get the type size of the sum of type sizes for all the individual tuple types -template struct type_count_min::value>::type> { - static constexpr int value{tuple_type_size_min()}; -}; - -/// definition of subtype count -template struct subtype_count_min { - static constexpr int value{is_mutable_container::value - ? ((type_count::value < expected_max_vector_size) ? type_count::value : 0) - : type_count_min::value}; +/// Type size of types that look like a vector +template struct type_count::value>::type> { + static constexpr int value{is_vector::value ? expected_max_vector_size + : type_count::value}; }; /// This will only trigger for actual void type @@ -1335,22 +1167,14 @@ template struct expected_count { static con /// For most types the number of expected items is 1 template -struct expected_count::value && !is_wrapper::value && - !std::is_void::value>::type> { +struct expected_count::value && !std::is_void::value>::type> { static constexpr int value{1}; }; /// number of expected items in a vector -template struct expected_count::value>::type> { +template struct expected_count::value>::type> { static constexpr int value{expected_max_vector_size}; }; -/// number of expected items in a vector -template -struct expected_count::value && is_wrapper::value>::type> { - static constexpr int value{expected_count::value}; -}; - // Enumeration of the different supported categorizations of objects enum class object_category : int { integral_value = 2, @@ -1361,15 +1185,12 @@ enum class object_category : int { number_constructible = 12, double_constructible = 14, integer_constructible = 16, - // string like types - string_assignable = 23, - string_constructible = 24, - other = 45, - // special wrapper or container types - wrapper_value = 50, - complex_number = 60, - tuple_value = 70, - container_value = 80, + vector_value = 30, + tuple_value = 35, + // string assignable or greater used in a condition so anything string like must come last + string_assignable = 50, + string_constructible = 60, + other = 200, }; @@ -1406,9 +1227,10 @@ template struct classify_object -struct classify_object::value && !std::is_integral::value && - std::is_assignable::value>::type> { +struct classify_object< + T, + typename std::enable_if::value && !std::is_integral::value && + std::is_assignable::value && !is_vector::value>::type> { static constexpr object_category value{object_category::string_assignable}; }; @@ -1417,8 +1239,8 @@ template struct classify_object< T, typename std::enable_if::value && !std::is_integral::value && - !std::is_assignable::value && (type_count::value == 1) && - std::is_constructible::value>::type> { + !std::is_assignable::value && + std::is_constructible::value && !is_vector::value>::type> { static constexpr object_category value{object_category::string_constructible}; }; @@ -1427,35 +1249,23 @@ template struct classify_object struct classify_object::value>::type> { - static constexpr object_category value{object_category::complex_number}; -}; - /// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point, /// vectors, and enumerations template struct uncommon_type { using type = typename std::conditional::value && !std::is_integral::value && !std::is_assignable::value && - !std::is_constructible::value && !is_complex::value && - !is_mutable_container::value && !std::is_enum::value, + !std::is_constructible::value && !is_vector::value && + !std::is_enum::value, std::true_type, std::false_type>::type; static constexpr bool value = type::value; }; -/// wrapper type -template -struct classify_object::value && is_wrapper::value && - !is_tuple_like::value && uncommon_type::value)>::type> { - static constexpr object_category value{object_category::wrapper_value}; -}; - /// Assignable from double or int template struct classify_object::value && type_count::value == 1 && - !is_wrapper::value && is_direct_constructible::value && + is_direct_constructible::value && is_direct_constructible::value>::type> { static constexpr object_category value{object_category::number_constructible}; }; @@ -1464,7 +1274,7 @@ struct classify_object struct classify_object::value && type_count::value == 1 && - !is_wrapper::value && !is_direct_constructible::value && + !is_direct_constructible::value && is_direct_constructible::value>::type> { static constexpr object_category value{object_category::integer_constructible}; }; @@ -1473,30 +1283,24 @@ struct classify_object struct classify_object::value && type_count::value == 1 && - !is_wrapper::value && is_direct_constructible::value && + is_direct_constructible::value && !is_direct_constructible::value>::type> { static constexpr object_category value{object_category::double_constructible}; }; /// Tuple type template -struct classify_object< - T, - typename std::enable_if::value && - ((type_count::value >= 2 && !is_wrapper::value) || - (uncommon_type::value && !is_direct_constructible::value && - !is_direct_constructible::value))>::type> { +struct classify_object::value >= 2 && !is_vector::value) || + (is_tuple_like::value && uncommon_type::value && + !is_direct_constructible::value && + !is_direct_constructible::value)>::type> { static constexpr object_category value{object_category::tuple_value}; - // the condition on this class requires it be like a tuple, but on some compilers (like Xcode) tuples can be - // constructed from just the first element so tuples of can be constructed from a string, which - // could lead to issues so there are two variants of the condition, the first isolates things with a type size >=2 - // mainly to get tuples on Xcode with the exception of wrappers, the second is the main one and just separating out - // those cases that are caught by other object classifications }; -/// container type -template struct classify_object::value>::type> { - static constexpr object_category value{object_category::container_value}; +/// Vector type +template struct classify_object::value>::type> { + static constexpr object_category value{object_category::vector_value}; }; // Type name print @@ -1542,53 +1346,31 @@ constexpr const char *type_name() { return "BOOLEAN"; } -/// Print name for enumeration types -template ::value == object_category::complex_number, detail::enabler> = detail::dummy> -constexpr const char *type_name() { - return "COMPLEX"; -} - /// Print for all other types template ::value >= object_category::string_assignable && - classify_object::value <= object_category::other, - detail::enabler> = detail::dummy> + enable_if_t::value >= object_category::string_assignable, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "TEXT"; } -/// typename for tuple value -template ::value == object_category::tuple_value && type_count_base::value >= 2, - detail::enabler> = detail::dummy> -std::string type_name(); // forward declaration - -/// Generate type name for a wrapper or container value -template ::value == object_category::container_value || - classify_object::value == object_category::wrapper_value, - detail::enabler> = detail::dummy> -std::string type_name(); // forward declaration /// Print name for single element tuple types template ::value == object_category::tuple_value && type_count_base::value == 1, + enable_if_t::value == object_category::tuple_value && type_count::value == 1, detail::enabler> = detail::dummy> inline std::string type_name() { - return type_name::type>::type>(); + return type_name::type>(); } /// Empty string if the index > tuple size template -inline typename std::enable_if::value, std::string>::type tuple_name() { +inline typename std::enable_if::value, std::string>::type tuple_name() { return std::string{}; } /// Recursively generate the tuple type name template -inline typename std::enable_if<(I < type_count_base::value), std::string>::type tuple_name() { - std::string str = std::string(type_name::type>::type>()) + - ',' + tuple_name(); + inline typename std::enable_if < I::value, std::string>::type tuple_name() { + std::string str = std::string(type_name::type>()) + ',' + tuple_name(); if(str.back() == ',') str.pop_back(); return str; @@ -1596,19 +1378,17 @@ inline typename std::enable_if<(I < type_count_base::value), std::string>::ty /// Print type name for tuples with 2 or more elements template ::value == object_category::tuple_value && type_count_base::value >= 2, - detail::enabler>> -inline std::string type_name() { + enable_if_t::value == object_category::tuple_value && type_count::value >= 2, + detail::enabler> = detail::dummy> +std::string type_name() { auto tname = std::string(1, '[') + tuple_name(); tname.push_back(']'); return tname; } -/// get the type name for a type that has a value_type member +/// This one should not be used normally, since vector types print the internal type template ::value == object_category::container_value || - classify_object::value == object_category::wrapper_value, - detail::enabler>> + enable_if_t::value == object_category::vector_value, detail::enabler> = detail::dummy> inline std::string type_name() { return type_name(); } @@ -1726,38 +1506,6 @@ bool lexical_cast(const std::string &input, T &output) { } } -/// complex -template ::value == object_category::complex_number, detail::enabler> = detail::dummy> -bool lexical_cast(const std::string &input, T &output) { - using XC = typename wrapped_type::type; - XC x{0.0}, y{0.0}; - auto str1 = input; - bool worked = false; - auto nloc = str1.find_last_of("+-"); - if(nloc != std::string::npos && nloc > 0) { - worked = detail::lexical_cast(str1.substr(0, nloc), x); - str1 = str1.substr(nloc); - if(str1.back() == 'i' || str1.back() == 'j') - str1.pop_back(); - worked = worked && detail::lexical_cast(str1, y); - } else { - if(str1.back() == 'i' || str1.back() == 'j') { - str1.pop_back(); - worked = detail::lexical_cast(str1, y); - x = XC{0}; - } else { - worked = detail::lexical_cast(str1, x); - y = XC{0}; - } - } - if(worked) { - output = T{x, y}; - return worked; - } - return from_stream(input, output); -} - /// String and similar direct assignment template ::value == object_category::string_assignable, detail::enabler> = detail::dummy> @@ -1788,18 +1536,6 @@ bool lexical_cast(const std::string &input, T &output) { return true; } -/// wrapper types -template ::value == object_category::wrapper_value, detail::enabler> = detail::dummy> -bool lexical_cast(const std::string &input, T &output) { - typename T::value_type val; - if(lexical_cast(input, val)) { - output = T{val}; - return true; - } - return from_stream(input, output); -} - /// Assignable from double or int template < typename T, @@ -1855,40 +1591,38 @@ bool lexical_cast(const std::string &input, T &output) { } /// Assign a value through lexical cast operations -/// Strings can be empty so we need to do a little different -template ::value && - (classify_object::value == object_category::string_assignable || - classify_object::value == object_category::string_constructible), - detail::enabler> = detail::dummy> -bool lexical_assign(const std::string &input, AssignTo &output) { +template < + typename T, + typename XC, + enable_if_t::value && (classify_object::value == object_category::string_assignable || + classify_object::value == object_category::string_constructible), + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, T &output) { return lexical_cast(input, output); } /// Assign a value through lexical cast operations -template ::value && - classify_object::value != object_category::string_assignable && - classify_object::value != object_category::string_constructible, +template ::value && classify_object::value != object_category::string_assignable && + classify_object::value != object_category::string_constructible, detail::enabler> = detail::dummy> -bool lexical_assign(const std::string &input, AssignTo &output) { +bool lexical_assign(const std::string &input, T &output) { if(input.empty()) { - output = AssignTo{}; + output = T{}; return true; } return lexical_cast(input, output); } /// Assign a value converted from a string in lexical cast to the output value directly -template ::value && std::is_assignable::value, - detail::enabler> = detail::dummy> -bool lexical_assign(const std::string &input, AssignTo &output) { - ConvertTo val{}; - bool parse_result = (!input.empty()) ? lexical_cast(input, val) : true; +template < + typename T, + typename XC, + enable_if_t::value && std::is_assignable::value, detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, T &output) { + XC val{}; + bool parse_result = (!input.empty()) ? lexical_cast(input, val) : true; if(parse_result) { output = val; } @@ -1896,242 +1630,84 @@ bool lexical_assign(const std::string &input, AssignTo &output) { } /// Assign a value from a lexical cast through constructing a value and move assigning it -template < - typename AssignTo, - typename ConvertTo, - enable_if_t::value && !std::is_assignable::value && - std::is_move_assignable::value, - detail::enabler> = detail::dummy> -bool lexical_assign(const std::string &input, AssignTo &output) { - ConvertTo val{}; - bool parse_result = input.empty() ? true : lexical_cast(input, val); +template ::value && !std::is_assignable::value && + std::is_move_assignable::value, + detail::enabler> = detail::dummy> +bool lexical_assign(const std::string &input, T &output) { + XC val{}; + bool parse_result = input.empty() ? true : lexical_cast(input, val); if(parse_result) { - output = AssignTo(val); // use () form of constructor to allow some implicit conversions + output = T(val); // use () form of constructor to allow some implicit conversions } return parse_result; } - -/// primary lexical conversion operation, 1 string to 1 type of some kind -template ::value <= object_category::other && - classify_object::value <= object_category::wrapper_value, - detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - return lexical_assign(strings[0], output); +/// Lexical conversion if there is only one element +template < + typename T, + typename XC, + enable_if_t::value && !is_tuple_like::value && !is_vector::value && !is_vector::value, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { + return lexical_assign(strings[0], output); } -/// Lexical conversion if there is only one element but the conversion type is for two, then call a two element -/// constructor -template ::value <= 2) && expected_count::value == 1 && - is_tuple_like::value && type_count_base::value == 2, - detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - // the remove const is to handle pair types coming from a container - typename std::remove_const::type>::type v1; - typename std::tuple_element<1, ConvertTo>::type v2; +/// Lexical conversion if there is only one element but the conversion type is for two call a two element constructor +template ::value == 1 && type_count::value == 2, detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { + typename std::tuple_element<0, XC>::type v1; + typename std::tuple_element<1, XC>::type v2; bool retval = lexical_assign(strings[0], v1); if(strings.size() > 1) { retval = retval && lexical_assign(strings[1], v2); } if(retval) { - output = AssignTo{v1, v2}; + output = T{v1, v2}; } return retval; } -/// Lexical conversion of a container types of single elements -template ::value && is_mutable_container::value && - type_count::value == 1, +/// Lexical conversion of a vector types +template ::value == expected_max_vector_size && + expected_count::value == expected_max_vector_size && type_count::value == 1, detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - output.erase(output.begin(), output.end()); - for(const auto &elem : strings) { - typename AssignTo::value_type out; - bool retval = lexical_assign(elem, out); - if(!retval) { - return false; - } - output.insert(output.end(), std::move(out)); - } - return (!output.empty()); -} - -/// Lexical conversion for complex types -template ::value, detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - - if(strings.size() >= 2 && !strings[1].empty()) { - using XC2 = typename wrapped_type::type; - XC2 x{0.0}, y{0.0}; - auto str1 = strings[1]; - if(str1.back() == 'i' || str1.back() == 'j') { - str1.pop_back(); - } - auto worked = detail::lexical_cast(strings[0], x) && detail::lexical_cast(str1, y); - if(worked) { - output = ConvertTo{x, y}; - } - return worked; - } else { - return lexical_assign(strings[0], output); - } -} - -/// Conversion to a vector type using a particular single type as the conversion type -template ::value && (expected_count::value == 1) && - (type_count::value == 1), - detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - bool retval = true; +bool lexical_conversion(const std::vector &strings, T &output) { output.clear(); output.reserve(strings.size()); for(const auto &elem : strings) { output.emplace_back(); - retval = retval && lexical_assign(elem, output.back()); - } - return (!output.empty()) && retval; -} - -// forward declaration - -/// Lexical conversion of a container types with conversion type of two elements -template ::value && is_mutable_container::value && - type_count_base::value == 2, - detail::enabler> = detail::dummy> -bool lexical_conversion(std::vector strings, AssignTo &output); - -/// Lexical conversion of a vector types with type_size >2 forward declaration -template ::value && is_mutable_container::value && - type_count_base::value != 2 && - ((type_count::value > 2) || - (type_count::value > type_count_base::value)), - detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output); - -/// Conversion for tuples -template ::value && is_tuple_like::value && - (type_count_base::value != type_count::value || - type_count::value > 2), - detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output); // forward declaration - -/// Conversion for operations where the assigned type is some class but the conversion is a mutable container or large -/// tuple -template ::value && !is_mutable_container::value && - classify_object::value != object_category::wrapper_value && - (is_mutable_container::value || type_count::value > 2), - detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - - if(strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) { - ConvertTo val; - auto retval = lexical_conversion(strings, val); - output = AssignTo{val}; - return retval; - } - output = AssignTo{}; - return true; -} - -/// function template for converting tuples if the static Index is greater than the tuple size -template -inline typename std::enable_if<(I >= type_count_base::value), bool>::type -tuple_conversion(const std::vector &, AssignTo &) { - return true; -} - -/// Conversion of a tuple element where the type size ==1 and not a mutable container -template -inline typename std::enable_if::value && type_count::value == 1, bool>::type -tuple_type_conversion(std::vector &strings, AssignTo &output) { - auto retval = lexical_assign(strings[0], output); - strings.erase(strings.begin()); - return retval; -} - -/// Conversion of a tuple element where the type size !=1 but the size is fixed and not a mutable container -template -inline typename std::enable_if::value && (type_count::value > 1) && - type_count::value == type_count_min::value, - bool>::type -tuple_type_conversion(std::vector &strings, AssignTo &output) { - auto retval = lexical_conversion(strings, output); - strings.erase(strings.begin(), strings.begin() + type_count::value); - return retval; -} - -/// Conversion of a tuple element where the type is a mutable container or a type with different min and max type sizes -template -inline typename std::enable_if::value || - type_count::value != type_count_min::value, - bool>::type -tuple_type_conversion(std::vector &strings, AssignTo &output) { - - std::size_t index{subtype_count_min::value}; - const std::size_t mx_count{subtype_count::value}; - const std::size_t mx{(std::max)(mx_count, strings.size())}; - - while(index < mx) { - if(is_separator(strings[index])) { - break; + bool retval = lexical_assign(elem, output.back()); + if(!retval) { + return false; } - ++index; } - bool retval = lexical_conversion( - std::vector(strings.begin(), strings.begin() + static_cast(index)), output); - strings.erase(strings.begin(), strings.begin() + static_cast(index) + 1); - return retval; + return (!output.empty()); } -/// Tuple conversion operation -template -inline typename std::enable_if<(I < type_count_base::value), bool>::type -tuple_conversion(std::vector strings, AssignTo &output) { - bool retval = true; - using ConvertToElement = typename std:: - conditional::value, typename std::tuple_element::type, ConvertTo>::type; - if(!strings.empty()) { - retval = retval && tuple_type_conversion::type, ConvertToElement>( - strings, std::get(output)); - } - retval = retval && tuple_conversion(std::move(strings), output); - return retval; -} - -/// Lexical conversion of a container types with tuple elements of size 2 -template ::value && is_mutable_container::value && - type_count_base::value == 2, - detail::enabler>> -bool lexical_conversion(std::vector strings, AssignTo &output) { +/// Lexical conversion of a vector types with type size of two +template ::value == expected_max_vector_size && + expected_count::value == expected_max_vector_size && type_count::value == 2, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { output.clear(); - while(!strings.empty()) { + for(std::size_t ii = 0; ii < strings.size(); ii += 2) { - typename std::remove_const::type>::type v1; - typename std::tuple_element<1, typename ConvertTo::value_type>::type v2; - bool retval = tuple_type_conversion(strings, v1); - if(!strings.empty()) { - retval = retval && tuple_type_conversion(strings, v2); + typename std::tuple_element<0, typename XC::value_type>::type v1; + typename std::tuple_element<1, typename XC::value_type>::type v2; + bool retval = lexical_assign(strings[ii], v1); + if(strings.size() > ii + 1) { + retval = retval && lexical_assign(strings[ii + 1], v2); } if(retval) { - output.insert(output.end(), typename AssignTo::value_type{v1, v2}); + output.emplace_back(v1, v2); } else { return false; } @@ -2139,82 +1715,110 @@ bool lexical_conversion(std::vector strings, AssignTo &output) { return (!output.empty()); } -/// lexical conversion of tuples with type count>2 or tuples of types of some element with a type size>=2 -template ::value && is_tuple_like::value && - (type_count_base::value != type_count::value || - type_count::value > 2), - detail::enabler>> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - static_assert( - !is_tuple_like::value || type_count_base::value == type_count_base::value, - "if the conversion type is defined as a tuple it must be the same size as the type you are converting to"); - return tuple_conversion(strings, output); +/// Conversion to a vector type using a particular single type as the conversion type +template ::value == expected_max_vector_size) && (expected_count::value == 1) && + (type_count::value == 1), + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { + bool retval = true; + output.clear(); + output.reserve(strings.size()); + for(const auto &elem : strings) { + + output.emplace_back(); + retval = retval && lexical_assign(elem, output.back()); + } + return (!output.empty()) && retval; +} +// This one is last since it can call other lexical_conversion functions +/// Lexical conversion if there is only one element but the conversion type is a vector +template ::value && !is_vector::value && is_vector::value, detail::enabler> = + detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { + + if(strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) { + XC val; + auto retval = lexical_conversion(strings, val); + output = T{val}; + return retval; + } + output = T{}; + return true; } -/// Lexical conversion of a vector types for everything but tuples of two elements and types of size 1 -template ::value && is_mutable_container::value && - type_count_base::value != 2 && - ((type_count::value > 2) || - (type_count::value > type_count_base::value)), - detail::enabler>> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { +/// function template for converting tuples if the static Index is greater than the tuple size +template +inline typename std::enable_if= type_count::value, bool>::type tuple_conversion(const std::vector &, + T &) { + return true; +} +/// Tuple conversion operation +template + inline typename std::enable_if < + I::value, bool>::type tuple_conversion(const std::vector &strings, T &output) { + bool retval = true; + if(strings.size() > I) { + retval = retval && lexical_assign::type, + typename std::conditional::value, + typename std::tuple_element::type, + XC>::type>(strings[I], std::get(output)); + } + retval = retval && tuple_conversion(strings, output); + return retval; +} + +/// Conversion for tuples +template ::value, detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { + static_assert( + !is_tuple_like::value || type_count::value == type_count::value, + "if the conversion type is defined as a tuple it must be the same size as the type you are converting to"); + return tuple_conversion(strings, output); +} + +/// Lexical conversion of a vector types with type_size >2 +template ::value == expected_max_vector_size && + expected_count::value == expected_max_vector_size && (type_count::value > 2), + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, T &output) { bool retval = true; output.clear(); std::vector temp; - std::size_t ii{0}; - std::size_t icount{0}; - std::size_t xcm{type_count::value}; - auto ii_max = strings.size(); - while(ii < ii_max) { + std::size_t ii = 0; + std::size_t icount = 0; + std::size_t xcm = type_count::value; + while(ii < strings.size()) { temp.push_back(strings[ii]); ++ii; ++icount; - if(icount == xcm || is_separator(temp.back()) || ii == ii_max) { - if(static_cast(xcm) > type_count_min::value && is_separator(temp.back())) { + if(icount == xcm || temp.back().empty()) { + if(static_cast(xcm) == expected_max_vector_size) { temp.pop_back(); } - typename AssignTo::value_type temp_out; - retval = retval && - lexical_conversion(temp, temp_out); + output.emplace_back(); + retval = retval && lexical_conversion(temp, output.back()); temp.clear(); if(!retval) { return false; } - output.insert(output.end(), std::move(temp_out)); icount = 0; } } return retval; } - -/// conversion for wrapper types -template < - typename AssignTo, - class ConvertTo, - enable_if_t::value == object_category::wrapper_value, detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - if(strings.empty() || strings.front().empty()) { - output = ConvertTo{}; - return true; - } - typename ConvertTo::value_type val; - if(lexical_conversion(strings, val)) { - output = ConvertTo{val}; - return true; - } - return false; -} - /// 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 ::value, detail::enabler> = detail::dummy> +template ::value && std::is_unsigned::value, detail::enabler> = detail::dummy> void sum_flag_vector(const std::vector &flags, T &output) { std::int64_t count{0}; for(auto &flag : flags) { @@ -2228,7 +1832,8 @@ void sum_flag_vector(const std::vector &flags, T &output) { /// 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 ::value, detail::enabler> = detail::dummy> +template ::value && std::is_signed::value, detail::enabler> = detail::dummy> void sum_flag_vector(const std::vector &flags, T &output) { std::int64_t count{0}; for(auto &flag : flags) { @@ -2423,17 +2028,17 @@ class Config { virtual ~Config() = default; }; -/// This converter works with INI/TOML files; to write INI files use ConfigINI +/// This converter works with INI/TOML files; to write proper TOML files use ConfigTOML class ConfigBase : public Config { protected: /// the character used for comments - char commentChar = '#'; + char commentChar = ';'; /// the character used to start an array '\0' is a default to not use - char arrayStart = '['; + char arrayStart = '\0'; /// the character used to end an array '\0' is a default to not use - char arrayEnd = ']'; + char arrayEnd = '\0'; /// the character used to separate elements in an array - char arraySeparator = ','; + char arraySeparator = ' '; /// the character used separate the name from the value char valueDelimiter = '='; @@ -2465,18 +2070,18 @@ class ConfigBase : public Config { } }; -/// the default Config is the TOML file format -using ConfigTOML = ConfigBase; +/// the default Config is the INI file format +using ConfigINI = ConfigBase; -/// ConfigINI generates a "standard" INI compliant output -class ConfigINI : public ConfigTOML { +/// ConfigTOML generates a TOML compliant output +class ConfigTOML : public ConfigINI { public: - ConfigINI() { - commentChar = ';'; - arrayStart = '\0'; - arrayEnd = '\0'; - arraySeparator = ' '; + ConfigTOML() { + commentChar = '#'; + arrayStart = '['; + arrayEnd = ']'; + arraySeparator = ','; valueDelimiter = '='; } }; @@ -4012,7 +3617,7 @@ class Option : public OptionBase