CLI11
C++11 Command Line Interface Parser
TypeTools.hpp
1 #pragma once
2 
3 // Distributed under the 3-Clause BSD License. See accompanying
4 // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
5 
6 #include "StringTools.hpp"
7 #include <exception>
8 #include <memory>
9 #include <string>
10 #include <type_traits>
11 #include <vector>
12 
13 namespace CLI {
14 
15 // Type tools
16 
17 // Utilities for type enabling
18 namespace detail {
19 // Based generally on https://rmf.io/cxx11/almost-static-if
21 enum class enabler {};
22 
24 constexpr enabler dummy = {};
25 } // namespace detail
26 
32 template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
33 
35 template <typename... Ts> struct make_void { using type = void; };
36 
38 template <typename... Ts> using void_t = typename make_void<Ts...>::type;
39 
41 template <bool B, class T, class F> using conditional_t = typename std::conditional<B, T, F>::type;
42 
44 template <typename T> struct is_vector : std::false_type {};
45 
47 template <class T, class A> struct is_vector<std::vector<T, A>> : std::true_type {};
48 
50 template <typename T> struct is_bool : std::false_type {};
51 
53 template <> struct is_bool<bool> : std::true_type {};
54 
56 template <typename T> struct is_shared_ptr : std::false_type {};
57 
59 template <typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
60 
62 template <typename T> struct is_shared_ptr<const std::shared_ptr<T>> : std::true_type {};
63 
65 template <typename T> struct is_copyable_ptr {
66  static bool const value = is_shared_ptr<T>::value || std::is_pointer<T>::value;
67 };
68 
70 template <typename T> struct IsMemberType { using type = T; };
71 
73 template <> struct IsMemberType<const char *> { using type = std::string; };
74 
75 namespace detail {
76 
77 // These are utilities for IsMember
78 
81 template <typename T> struct element_type {
82  using type =
83  typename std::conditional<is_copyable_ptr<T>::value, typename std::pointer_traits<T>::element_type, T>::type;
84 };
85 
88 template <typename T> struct element_value_type { using type = typename element_type<T>::type::value_type; };
89 
91 template <typename T, typename _ = void> struct pair_adaptor : std::false_type {
92  using value_type = typename T::value_type;
93  using first_type = typename std::remove_const<value_type>::type;
94  using second_type = typename std::remove_const<value_type>::type;
95 
97  template <typename Q> static auto first(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
98  return std::forward<Q>(pair_value);
99  }
101  template <typename Q> static auto second(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
102  return std::forward<Q>(pair_value);
103  }
104 };
105 
108 template <typename T>
110  T,
111  conditional_t<false, void_t<typename T::value_type::first_type, typename T::value_type::second_type>, void>>
112  : std::true_type {
113  using value_type = typename T::value_type;
114  using first_type = typename std::remove_const<typename value_type::first_type>::type;
115  using second_type = typename std::remove_const<typename value_type::second_type>::type;
116 
118  template <typename Q> static auto first(Q &&pair_value) -> decltype(std::get<0>(std::forward<Q>(pair_value))) {
119  return std::get<0>(std::forward<Q>(pair_value));
120  }
122  template <typename Q> static auto second(Q &&pair_value) -> decltype(std::get<1>(std::forward<Q>(pair_value))) {
123  return std::get<1>(std::forward<Q>(pair_value));
124  }
125 };
126 
127 // Check for streamability
128 // Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream
129 
130 template <typename S, typename T> class is_streamable {
131  template <typename SS, typename TT>
132  static auto test(int) -> decltype(std::declval<SS &>() << std::declval<TT>(), std::true_type());
133 
134  template <typename, typename> static auto test(...) -> std::false_type;
135 
136  public:
137  static const bool value = decltype(test<S, T>(0))::value;
138 };
139 
141 template <typename T, enable_if_t<std::is_constructible<std::string, T>::value, detail::enabler> = detail::dummy>
142 auto to_string(T &&value) -> decltype(std::forward<T>(value)) {
143  return std::forward<T>(value);
144 }
145 
147 template <typename T,
148  enable_if_t<!std::is_constructible<std::string, T>::value && is_streamable<std::stringstream, T>::value,
149  detail::enabler> = detail::dummy>
150 std::string to_string(T &&value) {
151  std::stringstream stream;
152  stream << value;
153  return stream.str();
154 }
155 
157 template <typename T,
158  enable_if_t<!std::is_constructible<std::string, T>::value && !is_streamable<std::stringstream, T>::value,
159  detail::enabler> = detail::dummy>
160 std::string to_string(T &&) {
161  return std::string{};
162 }
163 
164 // Type name print
165 
169 
170 template <typename T,
171  enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
172 constexpr const char *type_name() {
173  return "INT";
174 }
175 
176 template <typename T,
177  enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
178 constexpr const char *type_name() {
179  return "UINT";
180 }
181 
182 template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
183 constexpr const char *type_name() {
184  return "FLOAT";
185 }
186 
188 template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
189 constexpr const char *type_name() {
190  return "VECTOR";
191 }
193 template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
194 constexpr const char *type_name() {
195  return "ENUM";
196 }
197 
199 template <typename T,
200  enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value &&
201  !std::is_enum<T>::value,
202  detail::enabler> = detail::dummy>
203 constexpr const char *type_name() {
204  return "TEXT";
205 }
206 
207 // Lexical cast
208 
210 inline int64_t to_flag_value(std::string val) {
211  static const std::string trueString("true");
212  static const std::string falseString("false");
213  if(val == trueString) {
214  return 1;
215  }
216  if(val == falseString) {
217  return -1;
218  }
219  val = detail::to_lower(val);
220  int64_t ret;
221  if(val.size() == 1) {
222  switch(val[0]) {
223  case '0':
224  case 'f':
225  case 'n':
226  case '-':
227  ret = -1;
228  break;
229  case '1':
230  case 't':
231  case 'y':
232  case '+':
233  ret = 1;
234  break;
235  case '2':
236  case '3':
237  case '4':
238  case '5':
239  case '6':
240  case '7':
241  case '8':
242  case '9':
243  ret = val[0] - '0';
244  break;
245  default:
246  throw std::invalid_argument("unrecognized character");
247  }
248  return ret;
249  }
250  if(val == trueString || val == "on" || val == "yes" || val == "enable") {
251  ret = 1;
252  } else if(val == falseString || val == "off" || val == "no" || val == "disable") {
253  ret = -1;
254  } else {
255  ret = std::stoll(val);
256  }
257  return ret;
258 }
259 
261 template <
262  typename T,
263  enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value && !is_bool<T>::value && !std::is_enum<T>::value,
264  detail::enabler> = detail::dummy>
265 bool lexical_cast(std::string input, T &output) {
266  try {
267  size_t n = 0;
268  long long output_ll = std::stoll(input, &n, 0);
269  output = static_cast<T>(output_ll);
270  return n == input.size() && static_cast<long long>(output) == output_ll;
271  } catch(const std::invalid_argument &) {
272  return false;
273  } catch(const std::out_of_range &) {
274  return false;
275  }
276 }
277 
279 template <typename T,
280  enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value && !is_bool<T>::value, detail::enabler> =
281  detail::dummy>
282 bool lexical_cast(std::string input, T &output) {
283  if(!input.empty() && input.front() == '-')
284  return false; // std::stoull happily converts negative values to junk without any errors.
285 
286  try {
287  size_t n = 0;
288  unsigned long long output_ll = std::stoull(input, &n, 0);
289  output = static_cast<T>(output_ll);
290  return n == input.size() && static_cast<unsigned long long>(output) == output_ll;
291  } catch(const std::invalid_argument &) {
292  return false;
293  } catch(const std::out_of_range &) {
294  return false;
295  }
296 }
297 
299 template <typename T, enable_if_t<is_bool<T>::value, detail::enabler> = detail::dummy>
300 bool lexical_cast(std::string input, T &output) {
301  try {
302  auto out = to_flag_value(input);
303  output = (out > 0);
304  return true;
305  } catch(const std::invalid_argument &) {
306  return false;
307  }
308 }
309 
311 template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
312 bool lexical_cast(std::string input, T &output) {
313  try {
314  size_t n = 0;
315  output = static_cast<T>(std::stold(input, &n));
316  return n == input.size();
317  } catch(const std::invalid_argument &) {
318  return false;
319  } catch(const std::out_of_range &) {
320  return false;
321  }
322 }
323 
325 template <typename T,
326  enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
327  std::is_assignable<T &, std::string>::value,
328  detail::enabler> = detail::dummy>
329 bool lexical_cast(std::string input, T &output) {
330  output = input;
331  return true;
332 }
333 
335 template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
336 bool lexical_cast(std::string input, T &output) {
337  typename std::underlying_type<T>::type val;
338  bool retval = detail::lexical_cast(input, val);
339  if(!retval) {
340  return false;
341  }
342  output = static_cast<T>(val);
343  return true;
344 }
345 
347 template <typename T,
348  enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
349  !std::is_assignable<T &, std::string>::value && !std::is_enum<T>::value,
350  detail::enabler> = detail::dummy>
351 bool lexical_cast(std::string input, T &output) {
352  std::istringstream is;
353 
354  is.str(input);
355  is >> output;
356  return !is.fail() && !is.rdbuf()->in_avail();
357 }
358 
363 template <typename T,
364  enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
365 void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
366  int64_t count{0};
367  for(auto &flag : flags) {
368  count += detail::to_flag_value(flag);
369  }
370  output = (count > 0) ? static_cast<T>(count) : T{0};
371 }
372 
377 template <typename T,
378  enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
379 void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
380  int64_t count{0};
381  for(auto &flag : flags) {
382  count += detail::to_flag_value(flag);
383  }
384  output = static_cast<T>(count);
385 }
386 
387 } // namespace detail
388 } // namespace CLI
Definition: App.hpp:29
static auto second(Q &&pair_value) -> decltype(std::get< 1 >(std::forward< Q >(pair_value)))
Get the second value (really just the underlying value)
Definition: TypeTools.hpp:122
Definition: TypeTools.hpp:81
Check to see if something is a vector (fail check by default)
Definition: TypeTools.hpp:44
Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost ...
Definition: TypeTools.hpp:91
Definition: TypeTools.hpp:88
Check to see if something is copyable pointer.
Definition: TypeTools.hpp:65
Definition: TypeTools.hpp:130
Check to see if something is a shared pointer.
Definition: TypeTools.hpp:56
A copy of std::void_t from C++17 (helper for C++11 and C++14)
Definition: TypeTools.hpp:35
Check to see if something is bool (fail check by default)
Definition: TypeTools.hpp:50
This can be specialized to override the type deduction for IsMember.
Definition: TypeTools.hpp:70
static auto first(Q &&pair_value) -> decltype(std::forward< Q >(pair_value))
Get the first value (really just the underlying value)
Definition: TypeTools.hpp:97
static auto first(Q &&pair_value) -> decltype(std::get< 0 >(std::forward< Q >(pair_value)))
Get the first value (really just the underlying value)
Definition: TypeTools.hpp:118
static auto second(Q &&pair_value) -> decltype(std::forward< Q >(pair_value))
Get the second value (really just the underlying value)
Definition: TypeTools.hpp:101