Turi Create  4.0
flexible_type_converter.hpp
1 /* Copyright © 2017 Apple Inc. All rights reserved.
2  *
3  * Use of this source code is governed by a BSD-3-clause license that can
4  * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5  */
6 #ifndef TURI_FLEXIBLE_TYPE_FLEXIBLE_TYPE_CONVERTER_HPP
7 #define TURI_FLEXIBLE_TYPE_FLEXIBLE_TYPE_CONVERTER_HPP
8 #include <vector>
9 #include <map>
10 #include <unordered_map>
11 #include <tuple>
12 #include <type_traits>
13 #include <core/util/code_optimization.hpp>
14 #include <core/data/flexible_type/type_traits.hpp>
15 #include <core/data/flexible_type/flexible_type.hpp>
16 #include <core/data/flexible_type/flexible_type_conversion_utilities.hpp>
17 
18 namespace turi {
19 
20 /** The primary functions/structs available for doing the flexible
21  * type conversion.
22  *
23  */
24 template <typename T> struct is_flexible_type_convertible;
25 template <typename T> static void convert_from_flexible_type(T& t, const flexible_type& f);
26 template <typename T> static void convert_to_flexible_type(flexible_type& f, T&& t);
27 template <typename T> static flexible_type convert_to_flexible_type(T&& t);
28 
29 namespace flexible_type_internals {
30 
31 ////////////////////////////////////////////////////////////////////////////////
32 
33 /** The conversion path is chosen based on a list of possible
34  * conversions ranked by priority, with the lowest numbered converter
35  * that matches the particular type chosen as the conversion path.
36  * The constants below declare the particular converters available,
37  * and the actual definitions -- defined as specialized structs --
38  * are given after that.
39  *
40  * The conversion mechanism works by testing the matches() constexpr
41  * condition on each numbered struct, then choosing the struct number
42  * that matches with no lower numbers matching. A static_assert
43  * failure occurs if there are no matches.
44  */
45 
46 // The obvious case of another flexible type.
47 static constexpr int CVTR__FLEXIBLE_TYPE_EXACT = 1;
48 
49 // Numeric types.
50 static constexpr int CVTR__FLOATING_POINT = 2;
51 static constexpr int CVTR__INTEGER = 3;
52 
53 // Strings.
54 static constexpr int CVTR__FLEX_STRING_CONVERTIBLE = 4;
55 
56 
57 // Vectors -- sequences of numeric elements that can be exactly
58 // converted into a flex_vec type. Prioritized above lists.
59 static constexpr int CVTR__FLEX_VEC_CONVERTIBLE_SEQUENCE = 5;
60 static constexpr int CVTR__FLEX_VEC_CONVERTIBLE_PAIR = 6;
61 static constexpr int CVTR__FLEX_VEC_CONVERTIBLE_TUPLE = 7;
62 
63 // Dictionaries -- sequences convertible to an exact match.
64 static constexpr int CVTR__FLEX_DICT_CONVERTIBLE_SEQUENCE = 8;
65 static constexpr int CVTR__FLEX_DICT_CONVERTIBLE_MAPS = 9;
66 
67 // Lists -- any other sequences that cannot be converted into a
68 // dictionary or vector but can be converted into a list of flexible
69 // type.
70 static constexpr int CVTR__FLEX_LIST_CONVERTIBLE_PAIR = 10;
71 static constexpr int CVTR__FLEX_LIST_CONVERTIBLE_TUPLE = 11;
72 static constexpr int CVTR__FLEX_LIST_CONVERTIBLE_SEQUENCE = 12;
73 
74 // Enum types.
75 static constexpr int CVTR__ENUM = 13;
76 
77 // The last value -- this tells the resolver how many options there
78 // are out there.
79 static constexpr int NUM_CONVERTER_STRUCTS = 14;
80 
81 /**
82  * Type conversion priority is done according to the lowest numbered
83  * ft_converter for which matches<T>() returns true. If no converter
84  * correctly converts things, then a static assert is raised if
85  * convert is attempted.
86  *
87  * If matches() is not true, then the get and set methods are never
88  * instantiated.
89  */
90 template <int> struct ft_converter {
91  template <typename T> static constexpr bool matches() { return false; }
92 
93  template <typename T> static inline void get(T& t, const flexible_type& v) {
94  static_assert(swallow_to_false<T>::value, "get with bad match.");
95  }
96 
97  template <typename T>
98  static inline void set(flexible_type& t, const T& v) {
99  static_assert(swallow_to_false<T>::value, "set with bad match.");
100  }
101 };
102 
103 ////////////////////////////////////////////////////////////////////////////////
104 //
105 // flexible_type
106 //
107 ////////////////////////////////////////////////////////////////////////////////
108 
109 /** straight flexible_type. Always given priority.
110  */
111 template <> struct ft_converter<CVTR__FLEXIBLE_TYPE_EXACT> {
112 
113  template <typename T> static constexpr bool matches() {
114  return (std::is_same<T, flexible_type>::value
115  || std::is_same<T, flex_undefined>::value
116  || std::is_same<T, flex_int>::value
117  || std::is_same<T, flex_float>::value
118  || std::is_same<T, flex_string>::value
119  || std::is_same<T, flex_vec>::value
120  || std::is_same<T, flex_list>::value
121  || std::is_same<T, flex_dict>::value
122  || std::is_same<T, flex_image>::value
123  || std::is_same<T, flex_date_time>::value);
124 
125  }
126 
127  static void get(flexible_type& dest, const flexible_type& src) {
128  dest = src;
129  }
130 
131  static void get(flex_float& dest, const flexible_type& src) {
132  if(src.get_type() == flex_type_enum::FLOAT) {
133  dest = src.get<flex_float>();
134  } else if(src.get_type() == flex_type_enum::INTEGER) {
135  dest = src.to<flex_int>();
136  } else {
137  throw_type_conversion_error(src, "numeric value");
138  ASSERT_UNREACHABLE();
139  }
140  }
141 
142  static void get(flex_int& dest, const flexible_type& src) {
143  if(src.get_type() == flex_type_enum::INTEGER) {
144  dest = src.get<flex_int>();
145  } else if(src.get_type() == flex_type_enum::FLOAT) {
146  flex_float v = src.get<flex_float>();
147  dest = static_cast<flex_int>(v);
148  if(UNLIKELY(v != dest)) {
149  throw_type_conversion_error(src, "integer value");
150  ASSERT_UNREACHABLE();
151  }
152  } else {
153  throw_type_conversion_error(src, "numeric integer value");
154  ASSERT_UNREACHABLE();
155  }
156  }
157 
158  static void get(flex_vec& dest, const flexible_type& src) {
159  if(src.get_type() == flex_type_enum::VECTOR) {
160  dest = src.get<flex_vec>();
161  } else if(src.get_type() == flex_type_enum::LIST) {
162  dest = src.to<flex_vec>();
163  } else {
164  throw_type_conversion_error(src, "vector of floats");
165  ASSERT_UNREACHABLE();
166  }
167  }
168 
169  static void get(flex_list& dest, const flexible_type& src) {
170  if(src.get_type() == flex_type_enum::LIST) {
171  dest = src.get<flex_list>();
172  } else if(src.get_type() == flex_type_enum::VECTOR) {
173  dest = src.to<flex_list>();
174  } else {
175  throw_type_conversion_error(src, "vector of floats");
176  ASSERT_UNREACHABLE();
177  }
178  }
179 
180  // Finally, a generic, strict method that fails if type is not matched exactly.
181  template <typename T>
182  static void get(T& dest, const flexible_type& src) {
183  if (type_to_enum<T>::value != src.get_type()) {
184  std::string errormsg = std::string("Expecting ")
186  + ". But we got a " + flex_type_enum_to_name(src.get_type());
187  throw(errormsg);
188  }
189  dest = src.get<T>();
190  }
191 
192 
193  // This is the only case where setting from a move expression makes sense
194  template <typename T>
195  static void set(flexible_type& dest, T&& src) {
196  dest = std::forward<T>(src);
197  }
198 };
199 
200 ////////////////////////////////////////////////////////////////////////////////
201 //
202 // numeric types.
203 //
204 ////////////////////////////////////////////////////////////////////////////////
205 
206 /** Any floating point values.
207  */
208 template <> struct ft_converter<CVTR__FLOATING_POINT> {
209 
210  template <typename T> static constexpr bool matches() {
211  return std::is_floating_point<T>::value;
212  }
213 
214  template <typename Float>
215  static void get(Float& dest, const flexible_type& src) {
216  if(src.get_type() == flex_type_enum::FLOAT) {
217  dest = static_cast<Float>(src.get<flex_float>());
218  } else if(src.get_type() == flex_type_enum::INTEGER) {
219  dest = static_cast<Float>(src.get<flex_int>());
220  } else {
221  throw_type_conversion_error(src, "numeric");
222  ASSERT_UNREACHABLE();
223  }
224  }
225 
226  template <typename Float>
227  static void set(Float& dest, const flexible_type& src) {
228  dest = flex_float(src);
229  }
230 };
231 
232 ////////////////////////////////////////////////////////////////////////////////
233 
234 /** Integer values.
235  */
236 template <> struct ft_converter<CVTR__INTEGER> {
237 
238  template <typename T> static constexpr bool matches() {
239  return std::is_integral<T>::value || std::is_same<T, bool>::value;
240  }
241 
242  template <typename Integer>
243  static void get(Integer& dest, const flexible_type& src) {
244  if(src.get_type() == flex_type_enum::FLOAT) {
245  flex_float v = src.get<flex_float>();
246  if(static_cast<Integer>(v) != v) {
247  throw_type_conversion_error(src, "integer / convertable float");
248  ASSERT_UNREACHABLE();
249  }
250  dest = static_cast<Integer>(v);
251  } else if(src.get_type() == flex_type_enum::INTEGER) {
252  dest = static_cast<Integer>(src.get<flex_int>());
253  } else {
254  throw_type_conversion_error(src, "integer");
255  ASSERT_UNREACHABLE();
256  }
257  }
258 
259  template <typename Integer>
260  static void set(Integer& dest, const flexible_type& src) {
261  dest = Integer(flex_int(src));
262  }
263 };
264 
265 ////////////////////////////////////////////////////////////////////////////////
266 //
267 // Strings
268 //
269 ////////////////////////////////////////////////////////////////////////////////
270 
271 /** Any string types not exactly flex_string.
272  */
273 template <> struct ft_converter<CVTR__FLEX_STRING_CONVERTIBLE> {
274 
275  template <typename T> static constexpr bool matches() {
276  return is_string<T>::value || std::is_same<T, const char*>::value;
277  }
278 
279  template <typename String>
280  static void get(String& dest, const flexible_type& src) {
281  if(src.get_type() == flex_type_enum::STRING) {
282  const flex_string& s = src.get<flex_string>();
283  dest.assign(s.begin(), s.end());
284  } else {
285  flex_string s = src.to<flex_string>();
286  dest.assign(s.begin(), s.end());
287  }
288  }
289 
290  static void get(const char*& dest, const flexible_type& src) {
291  if(src.get_type() == flex_type_enum::STRING) {
292  dest = src.get<flex_string>().c_str();
293  } else {
294  throw_type_conversion_error(src, "const char* (reference to temporary).");
295  ASSERT_UNREACHABLE();
296  }
297  }
298 
299  template <typename String>
300  static void set(flexible_type& dest, const String& src) {
301  dest = flex_string(src.begin(), src.end());
302  }
303 
304  static void set(flexible_type& dest, const char* src) {
305  dest = flex_string(src);
306  }
307 };
308 
309 ////////////////////////////////////////////////////////////////////////////////
310 //
311 // Vectors
312 //
313 ////////////////////////////////////////////////////////////////////////////////
314 
315 /** Any sequence containers (vectors, lists, deques, etc.) of floating
316  * point values.
317  */
318 template <> struct ft_converter<CVTR__FLEX_VEC_CONVERTIBLE_SEQUENCE> {
319 
320  template <typename V> static constexpr bool matches() {
321  typedef typename first_nested_type<V>::type U;
322 
323  return (is_sequence_container<V>::value
324  && (std::is_floating_point<U>::value || is_integer_in_4bytes<U>::value));
325  }
326 
327  template <typename Vector>
328  static void get(Vector& dest, const flexible_type& src) {
329  if(src.get_type() == flex_type_enum::VECTOR) {
330  const flex_vec& v = src.get<flex_vec>();
331  dest.assign(v.begin(), v.end());
332  } else if(src.get_type() == flex_type_enum::LIST) {
333  const flex_list& f = src.get<flex_list>();
334  dest.assign(f.begin(), f.end());
335  } else {
336  throw_type_conversion_error(src, "flex_vec");
337  ASSERT_UNREACHABLE();
338  }
339  }
340 
341  template <typename Vector>
342  static void set(flexible_type& dest, const Vector& src) {
343  dest = flex_vec(src.begin(), src.end());
344  }
345 };
346 
347 
348 ////////////////////////////////////////////////////////////////////////////////
349 
350 /** Any pairs of numeric values. This is different from the general
351  * pair of flex_type_converible values since this allows the the
352  * std::pair to convert to and from a 2-element flex_vec if the types
353  * in the std::pair are numeric. Internally, the logic is the same
354  * as the case below if they are not strictly doubles.
355  */
356 template <> struct ft_converter<CVTR__FLEX_VEC_CONVERTIBLE_PAIR> {
357 
358  template <typename T> static constexpr bool matches() {
359  typedef typename first_nested_type<T>::type U1;
360  typedef typename second_nested_type<T>::type U2;
361 
362  return (is_std_pair<T>::value
363  && std::is_floating_point<U1>::value
364  && std::is_floating_point<U2>::value);
365  }
366 
367  template <typename T, typename U>
368  static void get(std::pair<T, U>& dest, const flexible_type& src) {
369  if(src.get_type() == flex_type_enum::LIST) {
370  const flex_list& l = src.get<flex_list>();
371  if(l.size() != 2) {
372  throw_type_conversion_error(src, "2-element flex_list/flex_vec (list size != 2)");
373  ASSERT_UNREACHABLE();
374  }
375  convert_from_flexible_type(dest.first, l[0]);
376  convert_from_flexible_type(dest.second, l[1]);
377  } else if(src.get_type() == flex_type_enum::VECTOR) {
378  const flex_vec& v = src.get<flex_vec>();
379  if(v.size() != 2){
380  throw_type_conversion_error(src, "2-element flex_list/flex_vec (vector size != 2)");
381  ASSERT_UNREACHABLE();
382  }
383  dest.first = static_cast<T>(v[0]);
384  dest.second = static_cast<U>(v[1]);
385  } else {
386  throw_type_conversion_error(src, "2-element flex_list/flex_vec");
387  ASSERT_UNREACHABLE();
388  }
389  }
390 
391  template <typename T, typename U>
392  static void set(flexible_type& dest, const std::pair<T,U>& src) {
393  if(std::is_floating_point<T>::value && std::is_floating_point<U>::value) {
394  dest = flex_vec{flex_float(src.first), flex_float(src.second)};
395  } else {
396  dest = flex_list{convert_to_flexible_type(src.first), convert_to_flexible_type(src.second)};
397  }
398  }
399 };
400 
401 ////////////////////////////////////////////////////////////////////////////////
402 
403 /** std::tuple of numeric values.
404  */
405 template <> struct ft_converter<CVTR__FLEX_VEC_CONVERTIBLE_TUPLE> {
406 
407  template <typename T> static constexpr bool matches() {
408  return (is_tuple<T>::value && all_nested_true<std::is_arithmetic, T>::value);
409  }
410 
411  template <typename... Args>
412  static void get(std::tuple<Args...>& dest, const flexible_type& src) {
413  switch(src.get_type()) {
414  case flex_type_enum::LIST: {
415  const flex_list& d = src.get<flex_list>();
416 
417  if (d.size() != sizeof...(Args)) {
418  std::string errormsg =
419  std::string("Expecting a list or vector of length ")
420  + std::to_string(sizeof...(Args)) + ", but we got a list of length "
421  + std::to_string(d.size());
422  throw(errormsg);
423  }
424  pack_tuple(dest, d);
425  break;
426  }
427 
428  case flex_type_enum::VECTOR: {
429  const flex_vec& d = src.get<flex_vec>();
430  if (d.size() != sizeof...(Args)) {
431  std::string errormsg =
432  std::string("Expecting a list or vector of length ")
433  + std::to_string(sizeof...(Args)) + ", but we got a vector of length "
434  + std::to_string(d.size());
435  throw(errormsg);
436  }
437  pack_tuple(dest, d);
438  break;
439  }
440 
441  default: {
442  std::string errormsg =
443  std::string("Expecting a list or vector of length ")
444  + std::to_string(sizeof...(Args)) + ", but we got a "
445  + flex_type_enum_to_name(src.get_type());
446  throw(errormsg);
447  }
448  }
449  }
450 
451  template <typename... Args>
452  static void set(flexible_type& dest, const std::tuple<Args...> & src) {
453  flex_vec v(sizeof...(Args));
454  unpack_tuple(v, src);
455  dest = std::move(v);
456  }
457 };
458 
459 
460 
461 ////////////////////////////////////////////////////////////////////////////////
462 //
463 // Dictionaries
464 //
465 ////////////////////////////////////////////////////////////////////////////////
466 
467 /** Any sequence containers of pairs of flexible type convertable
468  * values (may be numeric). Put into a dictionary.
469  */
470 template <> struct ft_converter<CVTR__FLEX_DICT_CONVERTIBLE_SEQUENCE> {
471 
472  template <typename T> static constexpr bool matches() {
473  typedef typename first_nested_type<T>::type pair_type;
474 
475  return conditional_test<is_sequence_container<T>::value && is_std_pair<pair_type>::value,
476  is_flexible_type_convertible, pair_type>::value;
477  }
478 
479  template <typename T, typename U, template <typename...> class C>
480  static void get(C<std::pair<T, U> >& dest, const flexible_type& src) {
481  if(src.get_type() == flex_type_enum::DICT) {
482  const flex_dict& fd = src.get<flex_dict>();
483  dest.resize(fd.size());
484  for(size_t i = 0; i < fd.size(); ++i) {
485  convert_from_flexible_type(dest[i].first, fd[i].first);
486  convert_from_flexible_type(dest[i].second, fd[i].second);
487  }
488  } else if(src.get_type() == flex_type_enum::LIST) {
489  const flex_list& fl = src.get<flex_list>();
490  dest.resize(fl.size());
491  for(size_t i = 0; i < fl.size(); ++i) {
492  convert_from_flexible_type(dest[i], fl[i]);
493  }
494  } else {
495  throw_type_conversion_error(src, "flex_dict or flex_list of 2-element list/vectors");
496  ASSERT_UNREACHABLE();
497  }
498  }
499 
500  template <typename T, typename U, template <typename...> class C>
501  static void set(flexible_type& dest, const C<std::pair<T, U> >& src) {
502  flex_dict d(src.size());
503  for(size_t i = 0; i < src.size();++i) {
504  d[i] = std::make_pair(convert_to_flexible_type(src[i].first), convert_to_flexible_type(src[i].second));
505  }
506  dest = std::move(d);
507  }
508 };
509 
510 ////////////////////////////////////////////////////////////////////////////////
511 
512 /** std::map<T, U>, std::unordered_map<T, U>, or boost::unordered_map,
513  * with T and U convertable to flexible_type.
514  */
515 template <> struct ft_converter<CVTR__FLEX_DICT_CONVERTIBLE_MAPS> {
516 
517  template <typename T> static constexpr bool matches() {
518 
519  typedef typename first_nested_type<T>::type U1;
520  typedef typename second_nested_type<T>::type U2;
521 
522  return (is_map<T>::value
523  && conditional_test<is_map<T>::value, is_flexible_type_convertible, U1>::value
524  && conditional_test<is_map<T>::value, is_flexible_type_convertible, U2>::value);
525  }
526 
527  template <typename T>
528  static void get(T& dest, const flexible_type& src) {
529  std::pair<typename T::key_type, typename T::mapped_type> p;
530 
531  if(src.get_type() == flex_type_enum::DICT) {
532 
533  const flex_dict& fd = src.get<flex_dict>();
534 
535  for(size_t i = 0; i < fd.size(); ++i) {
536  convert_from_flexible_type(p.first, fd[i].first);
537  convert_from_flexible_type(p.second, fd[i].second);
538  dest.insert(std::move(p));
539  }
540  } else if(src.get_type() == flex_type_enum::LIST) {
541  const flex_list& l = src.get<flex_list>();
542  for(size_t i = 0; i < l.size(); ++i) {
543  convert_from_flexible_type(p, l[i]);
544  dest.insert(std::move(p));
545  }
546  } else {
547  throw_type_conversion_error(src, "flex_dict / list of 2-element flex_lists/flex_vec");
548  ASSERT_UNREACHABLE();
549  }
550  }
551 
552  template <typename T>
553  static void set(flexible_type& dest, const T& src) {
554  flex_dict fd;
555  fd.reserve(src.size());
556  for(const auto& p : src) {
557  fd.push_back({convert_to_flexible_type(p.first), convert_to_flexible_type(p.second)});
558  }
559  dest = std::move(fd);
560  }
561 };
562 
563 
564 ////////////////////////////////////////////////////////////////////////////////
565 //
566 // Lists.
567 //
568 ////////////////////////////////////////////////////////////////////////////////
569 
570 
571 /** std::pair of flexible_type convertable stuff. Note that a pair of
572  * numeric values is taken care of by the numeric case.
573  */
574 template <> struct ft_converter<CVTR__FLEX_LIST_CONVERTIBLE_PAIR> {
575 
576  template <typename T> static constexpr bool matches() {
577  typedef typename first_nested_type<T>::type U1;
578  typedef typename second_nested_type<T>::type U2;
579 
580  return (is_std_pair<T>::value
581  && conditional_test<is_std_pair<T>::value, is_flexible_type_convertible, U1>::value
582  && conditional_test<is_std_pair<T>::value, is_flexible_type_convertible, U2>::value);
583  }
584 
585  template <typename T, typename U>
586  static void get(std::pair<T, U>& dest, const flexible_type& src) {
587  if(src.get_type() == flex_type_enum::LIST) {
588  const flex_list& l = src.get<flex_list>();
589  if(l.size() != 2) {
590  throw_type_conversion_error(src, "2-element flex_list/flex_vec (list size != 2)");
591  ASSERT_UNREACHABLE();
592  }
593  convert_from_flexible_type(dest.first, l[0]);
594  convert_from_flexible_type(dest.second, l[1]);
595  } else {
596  throw_type_conversion_error(src, "2-element flex_list/flex_vec");
597  ASSERT_UNREACHABLE();
598  }
599  }
600 
601  template <typename T, typename U>
602  static void set(flexible_type& dest, const std::pair<T,U>& src) {
603  dest = flex_list{convert_to_flexible_type(src.first), convert_to_flexible_type(src.second)};
604  }
605 };
606 
607 ////////////////////////////////////////////////////////////////////////////////
608 
609 /** std::tuple of flexible_type-convertable values. (Note that tuples
610  * of arithmetic values are taken care of by the numeric tuple case).
611  */
612 template <> struct ft_converter<CVTR__FLEX_LIST_CONVERTIBLE_TUPLE> {
613 
614  template <typename T> static constexpr bool matches() {
615  return (is_tuple<T>::value && all_nested_true<is_flexible_type_convertible, T>::value);
616  }
617 
618  template <typename... Args>
619  static void get(std::tuple<Args...>& dest, const flexible_type& src) {
620  switch(src.get_type()) {
621  case flex_type_enum::LIST: {
622  const flex_list& d = src.get<flex_list>();
623 
624  if (d.size() != sizeof...(Args)) {
625  std::string errormsg =
626  std::string("Expecting a list or vector of length ")
627  + std::to_string(sizeof...(Args)) + ", but we got a list of length "
628  + std::to_string(d.size());
629  throw(errormsg);
630  }
631  pack_tuple(dest, d);
632  break;
633  }
634 
635  default: {
636  std::string errormsg =
637  std::string("Expecting a list or vector of length ")
638  + std::to_string(sizeof...(Args)) + ", but we got a "
639  + flex_type_enum_to_name(src.get_type());
640  throw(errormsg);
641  }
642  }
643  }
644 
645  template <typename... Args>
646  static void set(flexible_type& dest, const std::tuple<Args...> & src) {
647  flex_list v(sizeof...(Args));
648  unpack_tuple(v, src);
649  dest = std::move(v);
650  }
651 };
652 
653 ////////////////////////////////////////////////////////////////////////////////
654 
655 /** Any sequence container of values that are convertable to a
656  * flexible type, but for which the flex_dict and flex_vec converters
657  * do not apply.
658  */
659 template <> struct ft_converter<CVTR__FLEX_LIST_CONVERTIBLE_SEQUENCE> {
660 
661  template <typename T> static constexpr bool matches() {
662  return (conditional_test<
663  is_sequence_container<T>::value,
664  is_flexible_type_convertible, typename first_nested_type<T>::type>::value);
665  }
666 
667  template <typename FlexContainer>
668  static void get(FlexContainer& dest, const flexible_type& src) {
669  switch(src.get_type()) {
670  case flex_type_enum::LIST: {
671  const flex_list& fl = src.get<flex_list>();
672  dest.resize(fl.size());
673  auto it = dest.begin();
674  for(size_t i = 0; i < fl.size(); ++i, ++it) {
675  typename FlexContainer::value_type t;
676  convert_from_flexible_type(t, fl[i]);
677  *it = std::move(t);
678  }
679  break;
680  }
681  case flex_type_enum::VECTOR: {
682  const flex_vec& fv = src.get<flex_vec>();
683  dest.resize(fv.size());
684 
685  auto it = dest.begin();
686  for(size_t i = 0; i < fv.size(); ++i, ++it) {
687  typename FlexContainer::value_type t;
688  // To prevent difficult compiler-time issues on this
689  // conversion path, convert these through a flexible_type
690  // double first to get proper dynamic type checking. The
691  // fast path should be compiled in given the flattening
692  // here.
693  convert_from_flexible_type(t, flexible_type(fv[i]));
694  *it = std::move(t);
695  }
696  break;
697  }
698  default: {
699  throw_type_conversion_error(src, "flex_list");
700  ASSERT_UNREACHABLE();
701  }
702  }
703  }
704 
705  template <typename FlexContainer>
706  static void set(flexible_type& dest, const FlexContainer& src) {
707  flex_list fl(src.size());
708 
709  auto it = src.begin();
710  for(size_t i = 0; i < fl.size(); ++i, ++it) {
711  // This explicit clast here is to get around the vector<bool> reference class,
712  // when the type is actually bool.
713  typename first_nested_type<FlexContainer>::type v = *it;
714  fl[i] = convert_to_flexible_type(std::move(v));
715  }
716 
717  dest = std::move(fl);
718  }
719 };
720 
721 
722 
723 ////////////////////////////////////////////////////////////////////////////////
724 //
725 // Date time types
726 //
727 ////////////////////////////////////////////////////////////////////////////////
728 
729 // Handled by the exact case
730 
731 
732 ////////////////////////////////////////////////////////////////////////////////
733 //
734 // Enum types.
735 //
736 ////////////////////////////////////////////////////////////////////////////////
737 
738 /** Enum types.
739  */
740 template <> struct ft_converter<CVTR__ENUM> {
741 
742  template <typename T> static constexpr bool matches() {
743  return std::is_enum<T>::value;
744  }
745 
746  template <typename Enum>
747  static void get(Enum& dest, const flexible_type& src) {
748  if(src.get_type() == flex_type_enum::INTEGER) {
749  dest = static_cast<Enum>(src.get<flex_int>());
750  } else {
751  throw_type_conversion_error(src, "integer / enum.");
752  ASSERT_UNREACHABLE();
753  }
754  }
755 
756  template <typename Enum>
757  static void set(flexible_type& dest, const Enum& val) {
758  dest = static_cast<flex_int>(val);
759  }
760 };
761 
762 
763 ////////////////////////////////////////////////////////////////////////////////
764 //
765 // All the boilerplate code to make the above work well.
766 //
767 ////////////////////////////////////////////////////////////////////////////////
768 
769 template <int idx> struct ft_resolver {
770 
771  ////////////////////////////////////////////////////////////////////////////////
772  // Do any of the converters of this idx or lower match on this one?
773 
774  // This match is true
775  template <typename T> static constexpr bool any_match(
776  typename std::enable_if<ft_converter<idx>::template matches<typename base_type<T>::type>()>::type* = NULL) {
777  return true;
778  }
779 
780  // This match is false -- recurse
781  template <typename T> static constexpr bool any_match(
782  typename std::enable_if<!ft_converter<idx>::template matches<typename base_type<T>::type>()>::type* = NULL) {
783  return ft_resolver<idx - 1>::template any_match<T>();
784  }
785 
786  ////////////////////////////////////////////////////////////////////////////////
787  // Does this idx, and none before this one, match?
788 
789  template <typename T> static constexpr bool matches() {
790  return (ft_converter<idx>::template matches<typename base_type<T>::type>()
791  && !ft_resolver<idx - 1>::template any_match<T>());
792  }
793 
794  ////////////////////////////////////////////////////////////////////////////////
795  // Does this idx, and none before this one, match?
796 
797  template <typename T>
798  static void get(T& t, const flexible_type& v,
799  typename std::enable_if<matches<T>()>::type* = NULL) {
801  }
802 
803  template <typename T>
804  static void get(T& t, const flexible_type& v,
805  typename std::enable_if<!matches<T>()>::type* = NULL) {
806  ft_resolver<idx - 1>::get(t, v);
807  }
808 
809  template <typename T>
810  static void set(flexible_type& t, T&& v,
811  typename std::enable_if<matches<T>()>::type* = NULL) {
812  ft_converter<idx>::set(t, std::forward<T>(v));
813  }
814 
815  template <typename T>
816  static void set(flexible_type& t, T&& v,
817  typename std::enable_if<!matches<T>()>::type* = NULL) {
818  ft_resolver<idx - 1>::set(t, std::forward<T>(v));
819  }
820 };
821 
822 // The base case in which any_match finally returns false
823 template <> struct ft_resolver<0> {
824  template <typename T> static constexpr bool any_match() { return false; }
825  template <typename T> static constexpr bool matches() { return false; }
826  template <typename... T> static void set(T...) {}
827  template <typename... T> static void get(T...) {}
828 };
829 
830 }
831 
832 /**
833  * is_flexible_type_convertible<T>::value is true if T can be converted
834  * to and from a flexible_type via flexible_type_converter<T>.
835  */
836 template <typename T>
838  static constexpr bool value = flexible_type_internals::ft_resolver<
839  flexible_type_internals::NUM_CONVERTER_STRUCTS-1>::template any_match<typename base_type<T>::type>();
840 };
841 
842 template <typename T> GL_HOT_INLINE_FLATTEN
843 static void convert_from_flexible_type(T& t, const flexible_type& f) {
844  static_assert(is_flexible_type_convertible<T>::value, "Type not convertable from flexible_type.");
845 
846  flexible_type_internals::ft_resolver<flexible_type_internals::NUM_CONVERTER_STRUCTS-1>::get(t, f);
847 };
848 
849 template <typename T> GL_HOT_INLINE_FLATTEN
850 static void convert_to_flexible_type(flexible_type& f, T&& t) {
851  static_assert(is_flexible_type_convertible<T>::value, "Type not convertable to flexible_type.");
852 
853  flexible_type_internals::ft_resolver<flexible_type_internals::NUM_CONVERTER_STRUCTS-1>::set(f, std::forward<T>(t));
854 };
855 
856 template <typename T> GL_HOT_INLINE_FLATTEN
857 static flexible_type convert_to_flexible_type(T&& t) {
858  static_assert(is_flexible_type_convertible<T>::value, "Type not convertable to flexible_type.");
859 
860  flexible_type f;
861  flexible_type_internals::ft_resolver<flexible_type_internals::NUM_CONVERTER_STRUCTS-1>::set(f, std::forward<T>(t));
862  return f;
863 };
864 
865 /** A class that wraps the above functions in a convenient way for testing.
866  */
867 template <typename T>
869 
870  static constexpr bool value = is_flexible_type_convertible<T>::value;
871 
872  flexible_type set(const T& t) const { return convert_to_flexible_type(t); }
873  flexible_type set(T&& t) const { return convert_to_flexible_type(t); }
874  T get(const flexible_type& f) const {
875  T t;
876  convert_from_flexible_type(t, f);
877  return t;
878  }
879 };
880 
881 /** A convenience class that is true if all arguments are flexible type convertable.
882  */
883 template <typename... Args>
885  static constexpr bool value = all_true<is_flexible_type_convertible, Args...>::value;
886 };
887 
888 } // namespace turi
889 #endif
std::vector< double > flex_vec
const char * flex_type_enum_to_name(flex_type_enum en)
#define GL_HOT_INLINE_FLATTEN
std::string flex_string
std::vector< std::pair< flexible_type, flexible_type > > flex_dict
std::vector< flexible_type > flex_list