From 91c63a0d68c426099111bc8c05508bb08d9baa56 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Fri, 14 Feb 2020 16:59:15 -0800 Subject: [PATCH] more work on the lexical conversion templates --- include/CLI/App.hpp | 27 ++--------------- include/CLI/TypeTools.hpp | 61 +++++++++++++++++++++++++++++++++++---- tests/HelpersTest.cpp | 2 ++ tests/OptionTypeTest.cpp | 7 +++-- 4 files changed, 65 insertions(+), 32 deletions(-) diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index eaa09bae7..f41e13536 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -592,7 +592,7 @@ class App { /// Add option for assigning to a variable template ::value && !detail::is_complex::value, detail::enabler> = detail::dummy> + enable_if_t::value, detail::enabler> = detail::dummy> Option *add_option(std::string option_name, AssignTo &variable, ///< The variable to set std::string option_description = "", @@ -610,33 +610,12 @@ class App { // to structs used in the evaluation can be temporary so that would cause issues. auto Tcount = detail::type_count::value; auto XCcount = detail::type_count::value; - opt->type_size((std::max)(Tcount, XCcount)); + opt->type_size(detail::type_count_min::value,(std::max)(Tcount, XCcount)); opt->expected(detail::expected_count::value); opt->run_callback_for_default(); return opt; } - /// Add option for assigning to a complex variable - template ::value && detail::is_complex::value, detail::enabler> = detail::dummy> - Option *add_option(std::string option_name, - AssignTo &variable, ///< The variable to set - std::string option_description = "", - bool defaulted = false) { - - auto fun = [&variable](const CLI::results_t &res) { // comment for spacing - return detail::lexical_conversion(res, variable); - }; - - Option *opt = add_option(option_name, fun, option_description, defaulted, [&variable]() { - return CLI::detail::checked_to_string(variable); - }); - opt->type_name("COMPLEX"); - opt->type_size(1, 2)->delimiter('+')->expected(1)->run_callback_for_default(); - return opt; - } - /// Add option for assigning to a variable template ::value, detail::enabler> = detail::dummy> @@ -652,7 +631,7 @@ class App { return std::string{}; }); opt->type_name(detail::type_name()); - opt->type_size(detail::type_count::value); + opt->type_size(detail::type_count_min::value, detail::type_count::value); opt->expected(detail::expected_count::value); opt->run_callback_for_default(); return opt; diff --git a/include/CLI/TypeTools.hpp b/include/CLI/TypeTools.hpp index 83436105e..2600ad2f6 100644 --- a/include/CLI/TypeTools.hpp +++ b/include/CLI/TypeTools.hpp @@ -386,7 +386,7 @@ struct type_count< /// Type size for complex since it sometimes looks like a wrapper template struct type_count::value>::type> { - static constexpr int value{1}; + static constexpr int value{2}; }; /// Type size of types that look like a container @@ -422,6 +422,46 @@ template struct type_count() }; }; +/// 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< + T, + typename std::enable_if::value && !is_tuple_like::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> { + static constexpr int value{ 1 }; +}; + +/// Type size of types that look like a container +template struct type_count_min::value>::type> { + static constexpr int value{ is_mutable_container::value + ? 1 + : type_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 type_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() }; +}; + /// This will only trigger for actual void type template struct expected_count { static const int value{0}; }; @@ -984,7 +1024,7 @@ bool lexical_conversion(const std::vector &strings, AssignTo&outpu /// Lexical conversion if there is only one element but the conversion type is for two, then call a two element constructor template ::value == 1 && type_count::value == 2, detail::enabler> = detail::dummy> + enable_if_t<(type_count::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; @@ -1078,7 +1118,7 @@ template & strings, AssignTo& output); /// Conversion for tuples -template ::value && (type_count::value > 1), detail::enabler> = detail::dummy> +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); /// Lexical conversion if there is only one element but the conversion type is a mutable container @@ -1106,7 +1146,7 @@ inline typename std::enable_if<(I >= type_count_base::value), bool>::t } template -inline typename std::enable_if::value == 1, bool>::type tuple_type_conversion(std::vector& strings, AssignTo& output) +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()); @@ -1125,7 +1165,16 @@ template inline typename std::enable_if < is_mutable_container::value, bool>::type tuple_type_conversion(std::vector & strings, AssignTo & output) { auto retval = lexical_conversion(strings, output); - strings.clear(); + auto loc = std::find_if(strings.begin(), strings.end(), [](const auto& str) {return str.empty() || (str == "--"); }); + if (loc != strings.end()) + { + strings.erase(strings.begin(), loc + 1); + } + else + { + strings.clear(); + } + return retval; } @@ -1173,7 +1222,7 @@ template } /// Conversion for tuples -template ::value&& (type_count::value>1), detail::enabler>> + 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::value == type_count::value, diff --git a/tests/HelpersTest.cpp b/tests/HelpersTest.cpp index 36ddb12d3..918dcbbc2 100644 --- a/tests/HelpersTest.cpp +++ b/tests/HelpersTest.cpp @@ -65,6 +65,8 @@ TEST(TypeTools, type_size) { //three level tuples V = CLI::detail::type_count < std::tuple >> > ::value; EXPECT_EQ(V, 5); + V = CLI::detail::type_count < std::pair>>::value; + EXPECT_GT(V, 2000); } TEST(TypeTools, expected_count) { diff --git a/tests/OptionTypeTest.cpp b/tests/OptionTypeTest.cpp index 6ec04725a..c7013266c 100644 --- a/tests/OptionTypeTest.cpp +++ b/tests/OptionTypeTest.cpp @@ -682,7 +682,7 @@ template class TApp_container_tuple : public TApp { using tup_obj = std::tuple; using containerTypes_tuple = - ::testing::Types, std::deque, std::set, std::list, std::map>>; + ::testing::Types, std::deque, std::set, std::list, std::map>,std::unordered_map>>; TYPED_TEST_SUITE(TApp_container_tuple, containerTypes_tuple,); @@ -706,6 +706,8 @@ TYPED_TEST(TApp_container_tuple, containerTuple) { using icontainer1 = std::vector; using icontainer2 = std::list; using icontainer3 = std::set; +using icontainer4 = std::pair>; + using containerTypes_container = ::testing::Types, std::list, std::set, @@ -717,7 +719,8 @@ using containerTypes_container = ::testing::Types, std::vector, std::list, std::set, - std::deque>; + std::deque, +std::vector>; template class TApp_container_container : public TApp { public: