Turi Create  4.0
variant_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_UNITY_LIB_VARIANT_DETAIL_HPP
7 #define TURI_UNITY_LIB_VARIANT_DETAIL_HPP
8 #include <vector>
9 #include <map>
10 #include <unordered_map>
11 #include <tuple>
12 #include <type_traits>
13 #include <boost/mpl/contains.hpp>
14 #include <boost/mpl/range_c.hpp>
15 #include <boost/mpl/vector.hpp>
16 #include <boost/mpl/bool.hpp>
17 #include <boost/mpl/count.hpp>
18 #include <boost/mpl/transform.hpp>
19 #include <boost/fusion/algorithm/iteration/for_each.hpp>
20 #include <model_server/lib/variant.hpp>
21 #include <model_server/lib/unity_global_singleton.hpp>
22 #include <core/data/flexible_type/flexible_type_converter.hpp>
23 #include <model_server/lib/api/function_closure_info.hpp>
24 namespace turi {
25 
26 // forward declarations
27 class unity_sarray_base;
28 class unity_sframe_base;
29 class unity_sgraph_base;
30 class model_base;
31 class unity_sarray;
32 class unity_sframe;
33 class unity_sgraph;
34 class gl_sframe;
35 class gl_sarray;
36 class gl_sgraph;
37 class gl_gframe;
38 
39 extern int64_t USE_GL_DATATYPE;
40 
41 /**************************************************************************/
42 /* */
43 /* Some Helper Templates */
44 /* */
45 /**************************************************************************/
46 /**
47  * is_variant_member<T>::value is true if T is a type contained by the
48  * variant_type, and is false otherwise.
49  *
50  * \code
51  * is_variant_member<unity_sarray_base*>::value // true
52  * is_variant_member<size_t>::value // false
53  * is_variant_member<flexible_type>::value // true
54  * \endcode
55  */
56 template <typename T>
57 using is_variant_member = boost::mpl::contains<variant_type::types, T>;
58 
59 /**
60  * is_model_descendent<T>::value is true if *T is a descendent of
61  * model_base, or is model_base itself.
62  *
63  * \code
64  * is_model_descendent<flexible_type>::value // false
65  * is_model_descendent<svariant_converter_imple_model*>::value // true
66  * is_model_descendent<svariant_converter_imple_model>::value // false
67  * is_model_descendent<model_base*>::value // true
68  * \endcode
69  */
70 template <typename T>
71 using is_model_descendent = std::is_convertible<T*, model_base*>;
72 
73 template <typename T>
74 struct is_toolkit_builtin {
75  static constexpr bool value =
76  std::is_same<typename std::decay<T>::type, gl_sarray>::value ||
77  std::is_same<typename std::decay<T>::type, gl_sframe>::value ||
78  std::is_same<typename std::decay<T>::type, gl_sarray>::value ||
79  std::is_same<typename std::decay<T>::type, gl_sgraph>::value;
80 };
81 
82 
83 
84 // forward declaration
85 template <typename T>
87 
88 // forward declaration
89 template <typename... Args>
91 
92 /**************************************************************************/
93 /* */
94 /* variant_converter */
95 /* */
96 /**************************************************************************/
97 
98 /**
99  * The variant_converter<T> is a class which exposes two member functions.
100  * \code
101  * T variant_converter<T>::get(const variant_type&)
102  * variant_type variant_converter<T>::set(const T&)
103  * \endcode
104  *
105  * Essentially variant_converter::get converts from a variant type to an
106  * arbitrary type T, and variant_converter::set converts from an arbitrary type
107  * T to a variant_type.
108  *
109  * The key is to support as interesting types for T as possible.
110  * The following are currently supported:
111  * - variant_type
112  * - any direct member of variant_type:
113  * - std::shared_ptr<unity_sarray_base>
114  * - std::shared_ptr<unity_sframe_base>
115  * - std::shared_ptr<unity_sgraph_base>
116  * - std::shared_ptr<model_base>
117  * - std::vector<variant_type>
118  * - std::map<std::string, variant_type>
119  * - everything convertible to flexible_type (See flexible_type_conveter.hpp)
120  * - std::shared_ptr<unity_sarray>
121  * - std::shared_ptr<unity_sframe>
122  * - std::shared_ptr<unity_sgraph>
123  * - gl_sarray
124  * - gl_sframe
125  * - gl_sgraph
126  * - gl_gframe
127  * - std::shared_ptr<T> where T is a descendent of model_base
128  * - variant_type
129  * - Recursive cases
130  * - std::vector<T> where T is of any type in this list including the
131  * recursive cases.
132  * - std::map<std::string, T> where T is of any type in this list including
133  * the recursive cases.
134  * - std::unordered_map<std::string, T> where T are of any type in this list
135  * including the recurrsive cases.
136  * - std::pair<S, T> where S, T are of any type in this list including the
137  * recursive cases.
138  * - std::tuple<T...> where T... are of any type in this list including the
139  * recursive cases.
140  *
141  * variant_converter_implementation Details
142  * ----------------------
143  * The key difficulty is to make sure that every T matches *exactly* one case.
144  * Each case below is numbered, and we document here how each situation maps
145  * to each case.
146  *
147  * Base case.
148  * All failed templatizations will come here where compilation will fail.
149  */
150 template <typename T, class Enable = void>
152  static constexpr bool value = false;
153 };
154 
155 
156 /**
157  * Case 1: Cover everything convertible to flexible_type.
158  *
159  * All remaining cases *must* exclude this case.
160  */
161 template <typename T>
163  typename std::enable_if<is_flexible_type_convertible<T>::value>::type> {
164  static constexpr bool value = true;
165 
166  T get(const variant_type& val) {
167  flexible_type f;
168  try {
169  f = variant_get_ref<flexible_type>(val);
170  } catch(...) {
171  std::string errormsg =
172  std::string("Expecting a flexible_type. Got a ") +
173  get_variant_which_name(val.which());
174  std_log_and_throw(std::invalid_argument, errormsg);
175  }
176  return flexible_type_converter<T>().get(f);
177  }
178 
179  variant_type set(const T& val) {
180  return flexible_type_converter<T>().set(val);
181  }
182 };
183 
184 /**
185  * Case 2: Cover all direct members of variant_type.
186  * But not flexible_type since that is covered by case 1.
187  * - unity_sarray_base*
188  * - unity_sframe_base*
189  * - unity_sgraph_base*
190  * - model_base*
191  * - std::vector<variant_type>
192  * - std::map<std::string, variant_type>
193  */
194 template <typename T>
196  typename std::enable_if<(is_variant_member<T>::value &&
197  !std::is_same<T, flexible_type>::value) ||
198  std::is_same<T, variant_type>::value>::type> {
199  static constexpr bool value = true;
200  T get(const variant_type& val) {
201  return variant_get_ref<T>(val);
202  }
203  variant_type set(const T& val) {
204  return variant_type(val);
205  }
206 };
207 
208 /**
209  * Case 3: Cover variant_type itself.
210  */
211 template <>
213  static constexpr bool value = true;
214  variant_type get(const variant_type& val) {
215  return val;
216  }
217  variant_type set(const variant_type& val) {
218  return val;
219  }
220 };
221 
222 /**
223  * Case 4: Cover unity_sarray*.
224  * (note that this case is not covered by Case 2 since variant_type
225  * stores unity_sarray_base* and not unity_sarray*
226  */
227 template <>
228 struct variant_converter<std::shared_ptr<unity_sarray>, void> {
229  static constexpr bool value = true;
230  std::shared_ptr<unity_sarray> get(const variant_type& val);
231  variant_type set(std::shared_ptr<unity_sarray> val);
232 };
233 
234 /**
235  * Case 5: Cover unity_sframe*.
236  * (note that this case is not covered by Case 2 since variant_type
237  * stores unity_sframe_base* and not unity_sframe*
238  */
239 template <>
240 struct variant_converter<std::shared_ptr<unity_sframe>, void> {
241  static constexpr bool value = true;
242  std::shared_ptr<unity_sframe> get(const variant_type& val);
243  variant_type set(std::shared_ptr<unity_sframe> val);
244 };
245 
246 /**
247  * Case 6: Cover unity_sgraph*.
248  * (note that this case is not covered by Case 2 since variant_type
249  * stores unity_sgraph_base* and not unity_sgraph*
250  */
251 template <>
252 struct variant_converter<std::shared_ptr<unity_sgraph>, void> {
253  static constexpr bool value = true;
254  std::shared_ptr<unity_sgraph> get(const variant_type& val);
255  variant_type set(std::shared_ptr<unity_sgraph> val);
256 };
257 
258 
259 /**
260  * Case 7a: Cover pointer descendents of model_base.
261  *
262  * This case must not capture the case where T is itself model_base*
263  * since that case is already covered by case 2 (direct members of variant_type)
264  */
265 template <typename T>
266 struct variant_converter<std::shared_ptr<T>,
267  typename std::enable_if<is_model_descendent<T>::value &&
268  !std::is_same<T, model_base>::value &&
269  !is_toolkit_builtin<T>::value>::type> {
270  static constexpr bool value = true;
271  std::shared_ptr<T> get(const variant_type& val) {
272  return std::dynamic_pointer_cast<T>(variant_get_ref<std::shared_ptr<model_base>>(val));
273  }
274  variant_type set(const std::shared_ptr<T>& val) {
275  return variant_type(std::static_pointer_cast<model_base>(val));
276  }
277 };
278 
279 
280 /**
281  * Case 7b: Cover descendents of model_base.
282  *
283  * This case must not capture the case where T is itself model_base*
284  * since that case is already covered by case 2 (direct members of variant_type)
285  */
286 template <typename T>
288  typename std::enable_if<is_model_descendent<T>::value &&
289  !std::is_same<T, model_base>::value &&
290  !is_toolkit_builtin<T>::value>::type> {
291  static constexpr bool value = true;
292  T get(const variant_type& val) {
293  return *std::dynamic_pointer_cast<T>(variant_get_ref<std::shared_ptr<model_base>>(val));
294  }
295  variant_type set(const T& val) {
296  return variant_type(std::static_pointer_cast<model_base>(std::make_shared<T>(val)));
297  }
298 };
299 /**
300  * Case 8: std::vector<T> where T can be contained by a variant.
301  *
302  * This covers all the remaining std::vector<T> cases.
303  * It must be careful to exclude Case 2. This must be disabled for
304  * std::vector<variant_type>
305  * std::vector<variant_type>
306  */
307 template <typename T>
308 struct variant_converter<std::vector<T>,
309  typename std::enable_if<!is_flexible_type_convertible<std::vector<T>>::value &&
310  is_variant_convertible<T>::value &&
311  !is_variant_member<std::vector<T>>::value>::type> {
312  static constexpr bool value = true;
313  std::vector<T> get(const variant_type& val_) {
314  const variant_vector_type& val = variant_get_ref<variant_vector_type>(val_);
315  std::vector<T> ret(val.size());
316  for (size_t i = 0;i < val.size(); ++i) {
317  ret[i] = variant_converter<T>().get(val[i]);
318  }
319  return ret;
320  }
321  variant_type set(const std::vector<T>& val) {
322  variant_vector_type ret(val.size());
323  for (size_t i = 0;i < val.size(); ++i) {
324  ret[i] = variant_converter<T>().set(val[i]);
325  }
326  return variant_type(ret);
327  }
328 };
329 
330 /**
331  * Case 9: Covers the case std::map<std::string, T> for any T which
332  * is convertible to variant_type.
333  * It must be careful to exclude Case 2. This must be disabled for
334  * std::map<std::string, variant_type>.
335  */
336 template <typename T>
337 struct variant_converter<std::map<std::string, T>,
338  typename std::enable_if<!is_flexible_type_convertible<T>::value &&
339  is_variant_convertible<T>::value &&
340  !is_variant_member<std::map<std::string, T>>::value>::type> {
341  static constexpr bool value = true;
342  std::map<std::string, T> get(const variant_type& val_) {
343  const variant_map_type& val = variant_get_ref<variant_map_type>(val_);
344  std::map<std::string, T> ret;
345  for (const auto& elem: val) {
346  ret[elem.first] = variant_converter<T>().get(elem.second);
347  }
348  return ret;
349  }
350  variant_type set(const std::map<std::string, T>& val) {
351  variant_map_type ret;
352  for (const auto& elem: val) {
353  ret[elem.first] = variant_converter<T>().set(elem.second);
354  }
355  return variant_type(ret);
356  }
357 };
358 
359 
360 /**
361  * Case 10: Covers the case std::unordered_map<std::string, T> for any T which
362  * is convertible to variant_type.
363  */
364 template <typename T>
365 struct variant_converter<std::unordered_map<std::string, T>,
366  typename std::enable_if<!is_flexible_type_convertible<T>::value &&
367  is_variant_convertible<T>::value>::type> {
368  static constexpr bool value = true;
369  std::unordered_map<std::string, T> get(const variant_type& val_) {
370  const variant_map_type& val = variant_get_ref<variant_map_type>(val_);
371  std::unordered_map<std::string, T> ret;
372  for (const auto& elem: val) {
373  ret[elem.first] = variant_converter<T>().get(elem.second);
374  }
375  return ret;
376  }
377  variant_type set(const std::unordered_map<std::string, T>& val) {
378  variant_map_type ret;
379  for (const auto& elem: val) {
380  ret[elem.first] = variant_converter<T>().set(elem.second);
381  }
382  return variant_type(ret);
383  }
384 };
385 
386 /**
387  * Case 11: Covers the case std::pair<S, T> where S, T are convertible
388  * to variant_type.
389  */
390 template <typename S, typename T>
391 struct variant_converter<std::pair<S, T>,
392  typename std::enable_if<!is_flexible_type_convertible<T>::value &&
393  is_variant_convertible<T>::value>::type> {
394  static constexpr bool value = true;
395  std::pair<S, T> get(const variant_type& val) {
396  std::vector<variant_type> ret =
398  if (ret.size() != 2) {
399  std_log_and_throw(std::invalid_argument,
400  "Expecting an array of length 2");
401  }
402  return {variant_converter<S>().get(ret[0]),
403  variant_converter<T>().get(ret[1])};
404  }
405  variant_type set(const std::pair<S, T>& val) {
406  variant_vector_type ret;
407  ret.push_back(variant_converter<S>().set(val.first));
408  ret.push_back(variant_converter<T>().set(val.second));
409  return ret;
410  }
411 };
412 
413 
414 #ifndef DISABLE_SDK_TYPES
415 /**
416  * Case 12: gl datatypes
417  */
418 template <>
420  static constexpr bool value = true;
421  gl_sarray get(const variant_type& val);
422  variant_type set(gl_sarray val);
423 };
424 
425 template <>
426 struct variant_converter<gl_sframe, void> {
427  static constexpr bool value = true;
428  gl_sframe get(const variant_type& val);
429  variant_type set(gl_sframe val);
430 };
431 
432 template <>
433 struct variant_converter<gl_sgraph, void> {
434  static constexpr bool value = true;
435  gl_sgraph get(const variant_type& val);
436  variant_type set(gl_sgraph val);
437 };
438 
439 template <>
440 struct variant_converter<gl_gframe, void> {
441  static constexpr bool value = true;
442  gl_gframe get(const variant_type& val);
443  variant_type set(gl_gframe val);
444 };
445 #endif
446 
447 namespace variant_converter_impl {
448 
449 template <typename TupleType>
450 struct fill_tuple {
451  const std::vector<variant_type>* input;
452  mutable TupleType* tuple;
453  template<int n>
454  void operator()(boost::mpl::integral_c<int, n> t) const {
455  typedef typename std::decay<decltype(std::get<n>(*tuple))>::type TargetType;
456  std::get<n>(*tuple) = variant_converter<TargetType>().get(input->at(n));
457  }
458 };
459 
460 
461 template <typename TupleType>
462 struct fill_variant{
463  const TupleType* input;
464  mutable std::vector<variant_type>* output;
465  template<int n>
466  void operator()(boost::mpl::integral_c<int, n> t) const {
467  typedef typename std::decay<decltype(std::get<n>(*input))>::type TargetType;
468  output->at(n) = variant_converter<TargetType>().set(std::get<n>(*input));
469  }
470 };
471 
472 
473 /**
474  * Gets a callable toolkit function from a closure specification obtaining
475  * the function from the unity_global singleton's instance of the toolkit
476  * function registry.
477  */
478 std::function<variant_type(const std::vector<variant_type>&)>
479  get_toolkit_function_from_closure(const function_closure_info& closure);
480 
481 } // variant_converter_impl
482 
483 /**
484  * Case 12: std::tuple<T...> where T... are all convertible to variant_type
485  */
486 template <typename... Args>
487 struct variant_converter<std::tuple<Args...>,
488  typename std::enable_if<!all_flexible_type_convertible<Args...>::value &&
489  all_variant_convertible<Args...>::value>::type> {
490  static constexpr bool value = true;
491  std::tuple<Args...> get(const variant_type& val) {
492  std::vector<variant_type> cv =
494  if (cv.size() != sizeof...(Args)) {
495  std::string error_msg =
496  "Expecting an array of length " + std::to_string(sizeof...(Args));
497  std_log_and_throw(std::invalid_argument, error_msg);
498  }
499 
500  typename boost::mpl::range_c<int, 0, sizeof...(Args)>::type tuple_range;
501  variant_converter_impl::fill_tuple<std::tuple<Args...>> filler;
502  std::tuple<Args...> output;
503  filler.input = &cv;
504  filler.tuple = &output;
505  boost::fusion::for_each(tuple_range, filler);
506  return output;
507  }
508  variant_type set(const std::tuple<Args...> & val) {
509  typename boost::mpl::range_c<int, 0, sizeof...(Args)>::type tuple_range;
510  variant_converter_impl::fill_variant<std::tuple<Args...>> filler;
511  std::vector<variant_type> output;
512  filler.input = &val;
513  filler.output = &output;
514  output.resize(sizeof...(Args));
515  boost::fusion::for_each(tuple_range, filler);
516  return output;
517  }
518 };
519 
520 /**
521  * Case 13: Covers the case std::function<S(T...)> where S, T... are convertible
522  * to variant_type.
523  */
524 template <typename S, typename... Args>
525 struct variant_converter<std::function<S(Args...)>,
526  typename std::enable_if<is_variant_convertible<S>::value &&
527  all_variant_convertible<Args...>::value>::type> {
528  static constexpr bool value = true;
529  std::function<S(Args...)> get(const variant_type& val) {
530  function_closure_info closure;
531  closure = variant_get_ref<function_closure_info>(val);
532  auto native_execute_function =
533  variant_converter_impl::get_toolkit_function_from_closure(closure);
534  // ok. now we need to wrap the native function up into a function of the
535  // appropriate type. How do we do that?
536  return [=](Args... args)->S {
537  std::tuple<Args...> val{args...};
538  typename boost::mpl::range_c<int, 0, sizeof...(Args)>::type tuple_range;
539  variant_converter_impl::fill_variant<std::tuple<Args...>> filler;
540  filler.input = &val;
541  filler.output->resize(sizeof...(Args));
542  boost::fusion::for_each(tuple_range, filler);
543  return variant_converter<S>().get(native_execute_function(*(filler.output)));
544  };
545  }
546  variant_type set(const std::function<S(Args...)>& val) {
547  std_log_and_throw(std::invalid_argument,
548  "Cannot convert function to variant");
549  ASSERT_UNREACHABLE();
550  }
551 };
552 
553 
554 
555 /**
556  * is_variant_type_convertible<T>::value is true if T can be converted
557  * to and from a variant_type via variant_converter<T>.
558  */
559 template <typename T>
561  static constexpr bool value = variant_converter<T>::value;
562  typedef boost::mpl::bool_<value> type;
563 };
564 
565 /**
566  * all_flexible_type_convertible<Args...>::value is true if every type in
567  * Args can be converted to and from a flexible_type via
568  * flexible_type_convert<T>.
569  */
570 template <typename... Args>
572  typedef boost::mpl::vector<Args...> type_sequence;
573  // makes an mpl::vector<true_, false_ ....> where each element transforms
574  // each arg to whether it is flexible_type convertible
575  typedef typename boost::mpl::transform<type_sequence,
576  is_variant_convertible<boost::mpl::_1>>::type transformed_sequence;
577 
578  // the number of good types (number which are true)
579  typedef typename boost::mpl::count<transformed_sequence, boost::mpl::bool_<true>>::type num_good;
580 
581  // true if all args are convertible.
582  static constexpr bool value = (num_good::value == sizeof...(Args));
583 };
584 
585 }; // turicreate
586 
587 #endif
STL namespace.
boost::make_recursive_variant< flexible_type, std::shared_ptr< unity_sgraph_base >, dataframe_t, std::shared_ptr< model_base >, std::shared_ptr< unity_sframe_base >, std::shared_ptr< unity_sarray_base >, std::map< std::string, boost::recursive_variant_ >, std::vector< boost::recursive_variant_ >, boost::recursive_wrapper< function_closure_info > >::type variant_type
Definition: variant.hpp:24
boost::mpl::contains< variant_type::types, T > is_variant_member
std::is_convertible< T *, model_base * > is_model_descendent
std::string get_variant_which_name(int i)
Definition: variant.hpp:69