Skip to content

Commit

Permalink
try to work through some issues with complex and other containers.
Browse files Browse the repository at this point in the history
  • Loading branch information
phlptp committed Feb 10, 2020
1 parent 9efacb5 commit a573308
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 56 deletions.
23 changes: 22 additions & 1 deletion include/CLI/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,27 @@ class App {
return opt;
}

/// Add option for assigning to a variable
template <typename AssignTo,
enable_if_t<!std::is_const<AssignTo>::value, detail::enabler> = detail::dummy>
Option *add_option_no_stream(std::string option_name,
AssignTo &variable, ///< The variable to set
std::string option_description = "") {

auto fun = [&variable](const CLI::results_t &res) { // comment for spacing
return detail::lexical_conversion<AssignTo,AssignTo>(res, variable);
};

Option *opt = add_option(option_name, fun, option_description, false, []() {
return std::string{};
});
opt->type_name(detail::type_name<AssignTo>());
opt->type_size(detail::type_count<AssignTo>::value);
opt->expected(detail::expected_count<AssignTo>::value);
opt->run_callback_for_default();
return opt;
}

/// Add option for a callback of a specific type
template <typename T>
Option *add_option_function(std::string option_name,
Expand Down Expand Up @@ -746,7 +767,7 @@ class App {
/// 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<!detail::is_container<T>::value && !std::is_const<T>::value &&
enable_if_t<!detail::is_mutable_container<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>
Expand Down
178 changes: 133 additions & 45 deletions include/CLI/TypeTools.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,17 @@ template <typename T, typename S = std::istringstream> class is_istreamable {
static constexpr bool value = decltype(test<T, S>(0))::value;
};

/// Check for complex
template <typename T> class is_complex {
template <typename TT>
static auto test(int) -> decltype(std::declval<TT>().real(), std::declval<TT>().imag(), std::true_type());

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

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

/// Templated operation to get a value from a stream
template <typename T, enable_if_t<is_istreamable<T>::value, detail::enabler> = detail::dummy>
bool from_stream(const std::string &istring, T &obj) {
Expand All @@ -197,14 +208,14 @@ bool from_stream(const std::string & /*istring*/, T & /*obj*/) {
return false;
}

// check to see if an object is a container (fail by default)
template <typename T, typename _ = void> struct is_container : std::false_type {};
// check to see if an object is a mutable container (fail by default)
template <typename T, typename _ = void> struct is_mutable_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 en 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 <typename T>
struct is_container<
struct is_mutable_container<
T,
conditional_t<false,
void_t<typename T::value_type,
Expand All @@ -216,6 +227,22 @@ struct is_container<
void>>
: public conditional_t<std::is_constructible<T, std::string>::value, std::false_type, std::true_type> {};

// check to see if an object is a mutable container (fail by default)
template <typename T, typename _ = void> 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 en 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 <typename T>
struct is_readable_container<
T,
conditional_t<false,
void_t<decltype(std::declval<T>().end()),
decltype(std::declval<T>().begin())>,
void>>
: public std::true_type{};


// check to see if an object is a wrapper (fail by default)
template <typename T, typename _ = void> struct is_wrapper : std::false_type {};

Expand Down Expand Up @@ -261,22 +288,23 @@ std::string to_string(T &&value) {
}

/// 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_ostreamable<T>::value &&
!is_container<typename std::remove_reference<typename std::remove_const<T>::type>::type>::value,
detail::enabler> = detail::dummy>
template <
typename T,
enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
!is_readable_container<typename std::remove_const<T>::type>::value,
detail::enabler> = detail::dummy>
std::string to_string(T &&) {
return std::string{};
}

/// convert a vector to a string
template <typename T,
enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
is_container<typename std::remove_reference<typename std::remove_const<T>::type>::type>::value,
detail::enabler> = detail::dummy>
template <
typename T,
enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
is_readable_container<typename std::remove_const<T>::type>::value,
detail::enabler> = detail::dummy>
std::string to_string(T &&variable) {
std::vector<std::string> defaults;
defaults.reserve(variable.size());
auto cval = variable.begin();
auto end = variable.end();
while(cval != end) {
Expand Down Expand Up @@ -330,21 +358,21 @@ template <typename T> struct type_count<T, typename std::enable_if<is_tuple_like
/// Type size for regular object types that do not look like a tuple
template <typename T>
struct type_count<T,
typename std::enable_if<!is_container<T>::value && !is_wrapper<T>::value &&
typename std::enable_if<!is_mutable_container<T>::value && !is_wrapper<T>::value &&
!is_tuple_like<T>::value && !std::is_void<T>::value>::type> {
static constexpr int value{1};
};

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

/// Type size for wrapper type that are not containers
template <typename T>
struct type_count<T,
typename std::enable_if<!is_container<T>::value && is_wrapper<T>::value && !is_tuple_like<T>::value &&
typename std::enable_if<!is_mutable_container<T>::value && is_wrapper<T>::value && !is_tuple_like<T>::value &&
!std::is_void<T>::value>::type> {
static constexpr int value{type_count<typename T::value_type>::value};
};
Expand All @@ -354,11 +382,11 @@ template <typename T, typename Enable = void> struct expected_count { static con

/// For most types the number of expected items is 1
template <typename T>
struct expected_count<T, typename std::enable_if<!is_container<T>::value && !std::is_void<T>::value>::type> {
struct expected_count<T, typename std::enable_if<!is_mutable_container<T>::value && !std::is_void<T>::value>::type> {
static constexpr int value{1};
};
/// number of expected items in a vector
template <typename T> struct expected_count<T, typename std::enable_if<is_container<T>::value>::type> {
template <typename T> struct expected_count<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
static constexpr int value{expected_max_vector_size};
};

Expand All @@ -372,6 +400,7 @@ enum class object_category : int {
number_constructible = 12,
double_constructible = 14,
integer_constructible = 16,
complex_number=22,
tuple_value = 35,
wrapper_value = 39,
container_value = 40,
Expand Down Expand Up @@ -435,66 +464,69 @@ template <typename T> struct classify_object<T, typename std::enable_if<std::is_
static constexpr object_category value{object_category::enumeration};
};

template <typename T> struct classify_object<T, typename std::enable_if<is_complex<T>::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 <typename T> struct uncommon_type {
using type = typename std::conditional<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
!std::is_assignable<T &, std::string>::value &&
!std::is_constructible<T, std::string>::value &&
!is_container<T>::value && !std::is_enum<T>::value,
!std::is_constructible<T, std::string>::value && !is_complex<T>::value &&
!is_mutable_container<T>::value && !std::is_enum<T>::value,
std::true_type,
std::false_type>::type;
static constexpr bool value = type::value;
};

/// wrapper type
template <typename T>
struct classify_object<
T,
typename std::enable_if<(!is_container<T>::value && is_wrapper<T>::value && !is_tuple_like<T>::value &&
uncommon_type<T>::value)>::type> {
static constexpr object_category value{ object_category::wrapper_value };
struct classify_object<T,
typename std::enable_if<(!is_mutable_container<T>::value && is_wrapper<T>::value &&
!is_tuple_like<T>::value && uncommon_type<T>::value)>::type> {
static constexpr object_category value{object_category::wrapper_value};
};

/// Assignable from double or int
template <typename T>
struct classify_object<T,
typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 && !is_wrapper<T>::value &&
is_direct_constructible<T, double>::value &&
is_direct_constructible<T, int>::value>::type> {
static constexpr object_category value{ object_category::number_constructible };
typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
!is_wrapper<T>::value && is_direct_constructible<T, double>::value &&
is_direct_constructible<T, int>::value>::type> {
static constexpr object_category value{object_category::number_constructible};
};

/// Assignable from int
template <typename T>
struct classify_object<T,
typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 && !is_wrapper<T>::value &&
!is_direct_constructible<T, double>::value &&
is_direct_constructible<T, int>::value>::type> {
static constexpr object_category value{ object_category::integer_constructible };
typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
!is_wrapper<T>::value && !is_direct_constructible<T, double>::value &&
is_direct_constructible<T, int>::value>::type> {
static constexpr object_category value{object_category::integer_constructible};
};

/// Assignable from double
template <typename T>
struct classify_object<T,
typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 && !is_wrapper<T>::value &&
is_direct_constructible<T, double>::value &&
typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
!is_wrapper<T>::value && is_direct_constructible<T, double>::value &&
!is_direct_constructible<T, int>::value>::type> {
static constexpr object_category value{object_category::double_constructible};
};

/// Tuple type
template <typename T>
struct classify_object<T,
typename std::enable_if<(type_count<T>::value >= 2 && !is_wrapper<T>::value) ||
typename std::enable_if<(type_count<T>::value >= 2 && !is_wrapper<T>::value ) ||
(is_tuple_like<T>::value && uncommon_type<T>::value &&
!is_direct_constructible<T, double>::value &&
!is_direct_constructible<T, int>::value)>::type> {
static constexpr object_category value{object_category::tuple_value};
};

/// container type
template <typename T> struct classify_object<T, typename std::enable_if<is_container<T>::value>::type> {
template <typename T> struct classify_object<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
static constexpr object_category value{object_category::container_value};
};

Expand Down Expand Up @@ -541,6 +573,13 @@ constexpr const char *type_name() {
return "BOOLEAN";
}

/// Print name for enumeration types
template <typename T,
enable_if_t<classify_object<T>::value == object_category::complex_number, detail::enabler> = detail::dummy>
constexpr const char *type_name() {
return "COMPLEX";
}

/// Print for all other types
template <typename T,
enable_if_t<classify_object<T>::value >= object_category::string_assignable, detail::enabler> = detail::dummy>
Expand Down Expand Up @@ -703,6 +742,41 @@ bool lexical_cast(const std::string &input, T &output) {
}
}

/// complex
template <typename T,
enable_if_t<classify_object<T>::value == object_category::complex_number, detail::enabler> = detail::dummy>
bool lexical_cast(const std::string &input, T &output) {
using XC=typename conditional_t<is_wrapper<T>::value, typename T::value_type, double>;
XC x, y;
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 <typename T,
enable_if_t<classify_object<T>::value == object_category::string_assignable, detail::enabler> = detail::dummy>
Expand Down Expand Up @@ -735,12 +809,11 @@ bool lexical_cast(const std::string &input, T &output) {

/// wrapper types
template <typename T,
enable_if_t<classify_object<T>::value == object_category::wrapper_value, detail::enabler> = detail::dummy>
bool lexical_cast(const std::string &input, T &output) {
enable_if_t<classify_object<T>::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 };
if(lexical_cast(input, val)) {
output = T{val};
return true;
}
return from_stream(input, output);
Expand Down Expand Up @@ -856,8 +929,9 @@ bool lexical_assign(const std::string &input, T &output) {
/// Lexical conversion if there is only one element
template <typename T,
typename XC,
enable_if_t<!is_tuple_like<T>::value && !is_tuple_like<XC>::value && !is_container<T>::value &&
!is_container<XC>::value,
enable_if_t<!is_tuple_like<T>::value && !is_tuple_like<XC>::value && !is_mutable_container<T>::value &&
!(classify_object<XC>::value == object_category::wrapper_value ||
classify_object<XC>::value == object_category::container_value),
detail::enabler> = detail::dummy>
bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
return lexical_assign<T, XC>(strings[0], output);
Expand All @@ -880,6 +954,20 @@ bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
return retval;
}

/// wrapper types
template <typename T,
class XC,
enable_if_t<classify_object<XC>::value == object_category::wrapper_value && type_count<XC>::value == 1,
detail::enabler> = detail::dummy>
bool lexical_conversion(const std::vector<std::string> &strings, T &output) {
typename XC::value_type val;
if(lexical_conversion<typename XC::value_type, typename XC::value_type>(strings, val)) {
output = XC{val};
return true;
}
return false;
}

/// Lexical conversion of a vector types
template <class T,
class XC,
Expand Down Expand Up @@ -945,7 +1033,7 @@ bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
/// Lexical conversion if there is only one element but the conversion type is a vector
template <typename T,
typename XC,
enable_if_t<!is_tuple_like<T>::value && !is_container<T>::value && is_container<XC>::value, detail::enabler> =
enable_if_t<!is_tuple_like<T>::value && !is_mutable_container<T>::value && is_mutable_container<XC>::value, detail::enabler> =
detail::dummy>
bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {

Expand Down
Loading

0 comments on commit a573308

Please sign in to comment.