Skip to content

Commit

Permalink
more work on the lexical conversion templates
Browse files Browse the repository at this point in the history
  • Loading branch information
phlptp committed Feb 15, 2020
1 parent bb927e2 commit 91c63a0
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 32 deletions.
27 changes: 3 additions & 24 deletions include/CLI/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ class App {
/// Add option for assigning to a variable
template <typename AssignTo,
typename ConvertTo = AssignTo,
enable_if_t<!std::is_const<ConvertTo>::value && !detail::is_complex<ConvertTo>::value, detail::enabler> = detail::dummy>
enable_if_t<!std::is_const<ConvertTo>::value, detail::enabler> = detail::dummy>
Option *add_option(std::string option_name,
AssignTo &variable, ///< The variable to set
std::string option_description = "",
Expand All @@ -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<AssignTo>::value;
auto XCcount = detail::type_count<ConvertTo>::value;
opt->type_size((std::max)(Tcount, XCcount));
opt->type_size(detail::type_count_min<ConvertTo>::value,(std::max)(Tcount, XCcount));
opt->expected(detail::expected_count<ConvertTo>::value);
opt->run_callback_for_default();
return opt;
}

/// Add option for assigning to a complex variable
template <typename AssignTo,
typename ConvertTo = AssignTo,
enable_if_t<!std::is_const<ConvertTo>::value && detail::is_complex<ConvertTo>::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<AssignTo, ConvertTo>(res, variable);
};

Option *opt = add_option(option_name, fun, option_description, defaulted, [&variable]() {
return CLI::detail::checked_to_string<AssignTo, ConvertTo>(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 <typename AssignTo,
enable_if_t<!std::is_const<AssignTo>::value, detail::enabler> = detail::dummy>
Expand All @@ -652,7 +631,7 @@ class App {
return std::string{};
});
opt->type_name(detail::type_name<AssignTo>());
opt->type_size(detail::type_count<AssignTo>::value);
opt->type_size(detail::type_count_min<AssignTo>::value, detail::type_count<AssignTo>::value);
opt->expected(detail::expected_count<AssignTo>::value);
opt->run_callback_for_default();
return opt;
Expand Down
61 changes: 55 additions & 6 deletions include/CLI/TypeTools.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ struct type_count<

/// Type size for complex since it sometimes looks like a wrapper
template <typename T> struct type_count<T, typename std::enable_if<is_complex<T>::value>::type> {
static constexpr int value{1};
static constexpr int value{2};
};

/// Type size of types that look like a container
Expand Down Expand Up @@ -422,6 +422,46 @@ template <typename T> struct type_count<T, typename std::enable_if<is_tuple_like
static constexpr int value{ tuple_type_size<T,0>() };
};

/// This will only trigger for actual void type
template <typename T, typename Enable = void> struct type_count_min { static const int value{ 0 }; };

/// Type size for regular object types that do not look like a tuple
template <typename T>
struct type_count_min<
T,
typename std::enable_if<!is_mutable_container<T>::value && !is_tuple_like<T>::value && !is_complex<T>::value && !std::is_void<T>::value>::type> {
static constexpr int value{ type_count<T>::value };
};

/// Type size for complex since it sometimes looks like a wrapper
template <typename T> struct type_count_min<T, typename std::enable_if<is_complex<T>::value>::type> {
static constexpr int value{ 1 };
};

/// Type size of types that look like a container
template <typename T> struct type_count_min<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
static constexpr int value{ is_mutable_container<typename T::value_type>::value
? 1
: type_count_min<typename T::value_type>::value };
};

/// 0 if the index > tuple size
template <typename T, std::size_t I>
constexpr typename std::enable_if<I == type_count_base<T>::value, int>::type tuple_type_size_min() {
return 0;
}

/// Recursively generate the tuple type name
template <typename T, std::size_t I>
constexpr typename std::enable_if < I<type_count_base<T>::value, int>::type tuple_type_size_min() {
return type_count_min<typename std::tuple_element<I, T>::type>::value + tuple_type_size_min<T, I + 1>();
}

/// Get the type size of the sum of type sizes for all the individual tuple types
template <typename T> struct type_count_min<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
static constexpr int value{ tuple_type_size_min<T,0>() };
};

/// This will only trigger for actual void type
template <typename T, typename Enable = void> struct expected_count { static const int value{0}; };

Expand Down Expand Up @@ -984,7 +1024,7 @@ bool lexical_conversion(const std::vector<std ::string> &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 <typename AssignTo,
typename ConvertTo,
enable_if_t<type_count<AssignTo>::value == 1 && type_count<ConvertTo>::value == 2, detail::enabler> = detail::dummy>
enable_if_t<(type_count<AssignTo>::value <= 2)&& expected_count<AssignTo>::value==1 &&is_tuple_like<ConvertTo>::value && type_count_base<ConvertTo>::value == 2, detail::enabler> = detail::dummy>
bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
//the remove const is to handle pair types coming from a container
typename std::remove_const<typename std::tuple_element<0, ConvertTo>::type>::type v1;
Expand Down Expand Up @@ -1078,7 +1118,7 @@ template <class AssignTo,
bool lexical_conversion(const std::vector<std::string>& strings, AssignTo& output);

/// Conversion for tuples
template <class AssignTo, class ConvertTo, enable_if_t<is_tuple_like<AssignTo>::value && (type_count<ConvertTo>::value > 1), detail::enabler> = detail::dummy>
template <class AssignTo, class ConvertTo, enable_if_t<is_tuple_like<AssignTo>::value && is_tuple_like<ConvertTo>::value && (type_count_base<ConvertTo>::value != type_count<ConvertTo>::value || type_count<ConvertTo>::value > 2), detail::enabler> = detail::dummy>
bool lexical_conversion(const std::vector<std::string>& strings, AssignTo& output);

/// Lexical conversion if there is only one element but the conversion type is a mutable container
Expand Down Expand Up @@ -1106,7 +1146,7 @@ inline typename std::enable_if<(I >= type_count_base<AssignTo>::value), bool>::t
}

template <class AssignTo, class ConvertTo>
inline typename std::enable_if<type_count<ConvertTo>::value == 1, bool>::type tuple_type_conversion(std::vector<std::string>& strings, AssignTo& output)
inline typename std::enable_if<!is_mutable_container<ConvertTo>::value && type_count<ConvertTo>::value == 1, bool>::type tuple_type_conversion(std::vector<std::string>& strings, AssignTo& output)
{
auto retval=lexical_assign<AssignTo, ConvertTo>(strings[0], output);
strings.erase(strings.begin());
Expand All @@ -1125,7 +1165,16 @@ template <class AssignTo, class ConvertTo>
inline typename std::enable_if < is_mutable_container<ConvertTo>::value, bool>::type tuple_type_conversion(std::vector<std::string> & strings, AssignTo & output)
{
auto retval = lexical_conversion<AssignTo, ConvertTo>(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;
}

Expand Down Expand Up @@ -1173,7 +1222,7 @@ template <class AssignTo, class ConvertTo, std::size_t I>
}

/// Conversion for tuples
template <class AssignTo, class ConvertTo, enable_if_t<is_tuple_like<AssignTo>::value&& (type_count<ConvertTo>::value>1), detail::enabler>>
template <class AssignTo, class ConvertTo, enable_if_t < is_tuple_like<AssignTo>::value && is_tuple_like<ConvertTo>::value && (type_count_base<ConvertTo>::value != type_count<ConvertTo>::value|| type_count<ConvertTo>::value>2), detail::enabler >>
bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo&output) {
static_assert(
!is_tuple_like<ConvertTo>::value || type_count<AssignTo>::value == type_count<ConvertTo>::value,
Expand Down
2 changes: 2 additions & 0 deletions tests/HelpersTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ TEST(TypeTools, type_size) {
//three level tuples
V = CLI::detail::type_count < std::tuple<int, std::pair<int, std::tuple<int,double,std::string> >> > ::value;
EXPECT_EQ(V, 5);
V = CLI::detail::type_count < std::pair<int, std::vector<int>>>::value;
EXPECT_GT(V, 2000);
}

TEST(TypeTools, expected_count) {
Expand Down
7 changes: 5 additions & 2 deletions tests/OptionTypeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ template <class T> class TApp_container_tuple : public TApp {

using tup_obj = std::tuple<int, std::string, double>;
using containerTypes_tuple =
::testing::Types<std::vector<tup_obj>, std::deque<tup_obj>, std::set<tup_obj>, std::list<tup_obj>, std::map<int,std::pair<std::string,double>>>;
::testing::Types<std::vector<tup_obj>, std::deque<tup_obj>, std::set<tup_obj>, std::list<tup_obj>, std::map<int,std::pair<std::string,double>>,std::unordered_map<int,std::tuple<std::string,double>>>;


TYPED_TEST_SUITE(TApp_container_tuple, containerTypes_tuple,);
Expand All @@ -706,6 +706,8 @@ TYPED_TEST(TApp_container_tuple, containerTuple) {
using icontainer1 = std::vector<int>;
using icontainer2 = std::list<int>;
using icontainer3 = std::set<int>;
using icontainer4 = std::pair<int, std::vector<int>>;

using containerTypes_container = ::testing::Types<std::vector<icontainer1>,
std::list<icontainer1>,
std::set<icontainer1>,
Expand All @@ -717,7 +719,8 @@ using containerTypes_container = ::testing::Types<std::vector<icontainer1>,
std::vector<icontainer3>,
std::list<icontainer3>,
std::set<icontainer3>,
std::deque<icontainer3>>;
std::deque<icontainer3>,
std::vector<icontainer4>>;

template <class T> class TApp_container_container : public TApp {
public:
Expand Down

0 comments on commit 91c63a0

Please sign in to comment.