Skip to content

Commit 6c2df1f

Browse files
committed
Factor out type utilities, single version of IsMember code
1 parent ea35587 commit 6c2df1f

File tree

2 files changed

+47
-76
lines changed

2 files changed

+47
-76
lines changed

include/CLI/TypeTools.hpp

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ namespace CLI {
1313

1414
// Type tools
1515

16+
// Utilities for type enabling
17+
namespace detail {
18+
// Based generally on https://rmf.io/cxx11/almost-static-if
19+
/// Simple empty scoped class
20+
enum class enabler {};
21+
22+
/// An instance to use in EnableIf
23+
constexpr enabler dummy = {};
24+
} // namespace detail
25+
1626
/// A copy of enable_if_t from C++14, compatible with C++11.
1727
///
1828
/// We could check to see if C++14 is being used, but it does not hurt to redefine this
@@ -44,13 +54,24 @@ template <typename T> struct is_copyable_ptr {
4454
static bool const value = is_shared_ptr<T>::value || std::is_pointer<T>::value;
4555
};
4656

47-
namespace detail {
48-
// Based generally on https://rmf.io/cxx11/almost-static-if
49-
/// Simple empty scoped class
50-
enum class enabler {};
57+
/// Handy helper to access the element_type generically. This is not part of is_copyable_ptr because it requires that
58+
/// pointer_traits<T> be valid.
59+
template <typename T> struct element_type {
60+
using type =
61+
typename std::conditional<is_copyable_ptr<T>::value, typename std::pointer_traits<T>::element_type, T>::type;
62+
};
5163

52-
/// An instance to use in EnableIf
53-
constexpr enabler dummy = {};
64+
/// Combination of the element type and value type - remove pointer (including smart pointers) and get the value_type of
65+
/// the container
66+
template <typename T> struct element_value_type { using type = typename element_type<T>::type::value_type; };
67+
68+
/// This can be specialized to override the type deduction for IsMember.
69+
template <typename T> struct IsMemberType { using type = T; };
70+
71+
/// The main custom type needed here is const char * should be a string.
72+
template <> struct IsMemberType<const char *> { using type = std::string; };
73+
74+
namespace detail {
5475

5576
// Type name print
5677

include/CLI/Validators.hpp

Lines changed: 20 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <functional>
1010
#include <iostream>
11+
#include <memory>
1112
#include <string>
1213

1314
// C standard library
@@ -264,11 +265,16 @@ class Range : public Validator {
264265
template <typename T> explicit Range(T max) : Range(static_cast<T>(0), max) {}
265266
};
266267

267-
/// This can be specialized to override the type deduction for IsMember.
268-
template <typename T> struct IsMemberType { using type = T; };
268+
namespace detail {
269+
template <typename T, enable_if_t<is_copyable_ptr<T>::value, detail::enabler> = detail::dummy>
270+
auto smart_deref(T value) -> decltype(*value) {
271+
return *value;
272+
}
269273

270-
/// The main custom type needed here is const char * should be a string.
271-
template <> struct IsMemberType<const char *> { using type = std::string; };
274+
template <typename T, enable_if_t<!is_copyable_ptr<T>::value, detail::enabler> = detail::dummy> T smart_deref(T value) {
275+
return value;
276+
}
277+
} // namespace detail
272278

273279
/// Verify items are in a set
274280
class IsMember : public Validator {
@@ -280,26 +286,18 @@ class IsMember : public Validator {
280286
explicit IsMember(std::initializer_list<T> values, Args &&... args)
281287
: IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}
282288

283-
/// This checks to see if an item is in a set: pointer version. (Empty function)
284-
template <typename T, enable_if_t<is_copyable_ptr<T>::value, detail::enabler> = detail::dummy>
285-
explicit IsMember(T set)
286-
: IsMember(std::move(set),
287-
std::function<typename IsMemberType<typename std::pointer_traits<T>::element_type::value_type>::type(
288-
typename IsMemberType<typename std::pointer_traits<T>::element_type::value_type>::type)>{}) {}
289-
290-
/// This checks to see if an item is in a set: copy version. (Empty function)
291-
template <typename T, enable_if_t<!is_copyable_ptr<T>::value, detail::enabler> = detail::dummy>
289+
/// This checks to see if an item is in a set (empty function)
290+
template <typename T>
292291
explicit IsMember(T set)
293292
: IsMember(std::move(set),
294-
std::function<typename IsMemberType<typename T::value_type>::type(
295-
typename IsMemberType<typename T::value_type>::type)>()) {}
293+
std::function<typename IsMemberType<typename element_value_type<T>::type>::type(
294+
typename IsMemberType<typename element_value_type<T>::type>::type)>()) {}
296295

297-
/// This checks to see if an item is in a set: pointer version. You can pass in a function that will filter
296+
/// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter
298297
/// both sides of the comparison before computing the comparison.
299-
template <typename T, typename F, enable_if_t<is_copyable_ptr<T>::value, detail::enabler> = detail::dummy>
300-
explicit IsMember(T set, F filter_function) {
298+
template <typename T, typename F> explicit IsMember(T set, F filter_function) {
301299
// Get the type of the contained item - requires a container have ::value_type
302-
using item_t = typename std::pointer_traits<T>::element_type::value_type;
300+
using item_t = typename element_value_type<T>::type;
303301
using local_item_t = typename IsMemberType<item_t>::type;
304302

305303
// Make a local copy of the filter function, using a std::function if not one already
@@ -308,62 +306,14 @@ class IsMember : public Validator {
308306
// This is the type name for help, it will take the current version of the set contents
309307
tname_function = [set]() {
310308
std::stringstream out;
311-
out << detail::type_name<item_t>() << " in {" << detail::join(*set, ",") << "}";
309+
out << detail::type_name<item_t>() << " in {" << detail::join(detail::smart_deref(set), ",") << "}";
312310
return out.str();
313311
};
314312

315313
// This is the function that validates
316314
// It stores a copy of the set pointer-like, so shared_ptr will stay alive
317315
func = [set, filter_fn](std::string &input) {
318-
for(const item_t &v : *set) {
319-
local_item_t a = v;
320-
local_item_t b;
321-
if(!detail::lexical_cast(input, b))
322-
throw ValidationError(input); // name is added later
323-
324-
// The filter function might be empty, so don't filter if it is.
325-
if(filter_fn) {
326-
a = filter_fn(a);
327-
b = filter_fn(b);
328-
}
329-
330-
if(a == b) {
331-
// Make sure the version in the input string is identical to the one in the set
332-
// Requires std::stringstream << be supported on T.
333-
if(filter_fn) {
334-
std::stringstream out;
335-
out << v;
336-
input = out.str();
337-
}
338-
339-
// Return empty error string (success)
340-
return std::string();
341-
}
342-
}
343-
344-
// If you reach this point, the result was not found
345-
return input + " not in {" + detail::join(*set, ",") + "}";
346-
};
347-
}
348-
349-
/// This checks to see if an item is in a set: copy version.
350-
template <typename T, typename F, enable_if_t<!is_copyable_ptr<T>::value, detail::enabler> = detail::dummy>
351-
explicit IsMember(T set, F filter_function) {
352-
// Get the type of the contained item - requires a container have ::value_type
353-
using item_t = typename T::value_type;
354-
using local_item_t = typename IsMemberType<item_t>::type;
355-
356-
// Make a local copy of the filter function, using a std::function if not one already
357-
std::function<local_item_t(local_item_t)> filter_fn = filter_function;
358-
359-
// This is the type name for help, since the set contents can't change, we just capture this
360-
std::stringstream out;
361-
out << detail::type_name<item_t>() << " in {" << detail::join(set, ",") << "}";
362-
tname = out.str();
363-
364-
// This is the function that validates
365-
func = [set, filter_fn](std::string &input) {
366-
for(const item_t &v : set) {
316+
for(const item_t &v : detail::smart_deref(set)) {
367317
local_item_t a = v;
368318
local_item_t b;
369319
if(!detail::lexical_cast(input, b))
@@ -390,7 +340,7 @@ class IsMember : public Validator {
390340
}
391341

392342
// If you reach this point, the result was not found
393-
return input + " not in {" + detail::join(set, ",") + "}";
343+
return input + " not in {" + detail::join(detail::smart_deref(set), ",") + "}";
394344
};
395345
}
396346

0 commit comments

Comments
 (0)