Turi Create  4.0
toolkit_function_wrapper_impl.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_TOOLKIT_FUNCTION_WRAPPER_IMPL_HPP
7 #define TURI_TOOLKIT_FUNCTION_WRAPPER_IMPL_HPP
8 #include <type_traits>
9 #include <boost/mpl/vector.hpp>
10 #include <boost/mpl/size.hpp>
11 #include <boost/mpl/contains.hpp>
12 #include <boost/mpl/assert.hpp>
13 #include <boost/mpl/int.hpp>
14 #include <boost/mpl/placeholders.hpp>
15 #include <boost/mpl/range_c.hpp>
16 #include <boost/mpl/transform.hpp>
17 #include <boost/fusion/mpl.hpp>
18 #include <boost/fusion/mpl/at.hpp>
19 #include <boost/fusion/include/vector.hpp>
20 #include <boost/fusion/include/as_vector.hpp>
21 #include <boost/fusion/algorithm/iteration/for_each.hpp>
22 #include <boost/fusion/functional/invocation/invoke.hpp>
23 #include <boost/type_traits.hpp>
24 #include <boost/type_traits/function_traits.hpp>
25 #include <model_server/lib/toolkit_function_invocation.hpp>
26 #include <model_server/lib/toolkit_function_specification.hpp>
27 #include <model_server/lib/toolkit_util.hpp>
28 namespace turi {
29 namespace toolkit_function_wrapper_impl {
30 
31 /**
32  * Type function which given a function pointer, returns an mpl::vector
33  * of the function argument type list.
34  *
35  * (R (*)(T...)) --> boost::mpl::vector<T...>
36  *
37  * Example:
38  * \code
39  * int test(int, double) {
40  * ...
41  * }
42  *
43  * typedef typename function_args_to_mpl_vector<decltype(test)>::type fn_args_type;
44  * \endcode
45  * will have fn_args_types == boost::mpl::vector<int, double>
46  *
47  */
48 template <typename Fn>
50  template <typename R, typename... T>
51  static boost::mpl::vector<T...> function_args_to_mpl_vector_helper(R (*)(T...)) {
52  return boost::mpl::vector<T...>();
53  }
54 
55  typedef decltype(function_args_to_mpl_vector_helper(reinterpret_cast<Fn>(NULL))) type;
56 };
57 
58 
59 /**
60  * Provides a wrapper around the return type of a function call.
61  * For instance:
62  * \code
63  * int test() {
64  * ...
65  * }
66  * typedef boost::function_traits<decltype(test)> fntraits;
67  *
68  * result_of_function_wrapper<fntraits::result_type> res;
69  * res.call(test);
70  *
71  * // after which res.ret_value will contain the return value of res
72  * // (which is an int).
73  * \endcode
74  *
75  * The key reason for this struct is to handle the void return case
76  * where the result type of the function is void. This struct specializes that
77  * case by making ret_value an integer which is 0 prior to calling the function,
78  * and 1 after successful calling of the function.
79  *
80  * i.e.
81  * \code
82  * void nothing() {
83  * ...
84  * }
85  * typedef boost::function_traits<decltype(nothing)> fntraits;
86  *
87  * result_of_function_wrapper<fntraits::result_type> res;
88  * // at this point res.ret_value is an integer and has value 0
89  * try {
90  * res.call(nothing);
91  * } catch (...) {
92  * // function was called, but it didn't return properly,
93  * // res.ret_value is still 0
94  * }
95  * // function call was completed successfully. res.ret_value == 1
96  * \endcode
97  *
98  * Note that the function must take no arguments. If there are arguments,
99  * it can be wrapped in a lambda. For instance:
100  *
101  * \code
102  * void has_arguments(int) {
103  * ...
104  * }
105  * typedef boost::function_traits<decltype(has_arguments)> fntraits;
106  * result_of_function_wrapper<fntraits::result_type> res;
107  * res.call([](){ return has_arguments(5);});
108  *
109  * \endcode
110  */
111 template <typename T>
113  T ret_value;
114  static constexpr bool is_void = false;
115  template <typename F>
116  void call(F f) {
117  ret_value = f();
118  }
119 };
120 
121 /** specialization for the void return case;
122  * in which case the return value is an integer which is 0 if the function
123  * is not called, and 1 if the function is called.
124  */
125 template <>
127  int ret_value = 0;
128  static constexpr bool is_void = true;
129  template <typename F>
130  void call(F f) {
131  f();
132  ret_value = 1;
133  }
134 };
135 
136 
137 /**
138  * A boost fusion type function that achieves:
139  *
140  * T -> T*
141  */
143  template<typename Sig>
144  struct result;
145 
146  template<typename T>
147  struct result<make_pointer_to(T&)>
148  {
149  typedef T* type;
150  };
151 
152  template<typename T>
153  T* operator()(T& t) const
154  {
155  return &t;
156  }
157 };
158 
159 /**
160  * Reads a typed function parameter from a variant argument. In most cases, this
161  * just boils down to variant_get_value<T>.
162  */
163 template <typename T>
164 inline T read_arg(const variant_type& var) {
165  return variant_get_value<T>(var);
166 }
167 
168 /**
169  * Specializes read_arg<variant_map_type>. The Python integration must convert
170  * Python dictionaries to either variant_map_type or flexible_type without any
171  * knowledge of the type required on the C++ end. Python prefers flexible_type
172  * when both are possible, so the C++ side must convert if necessary.
173  */
174 template <>
175 inline variant_map_type read_arg<variant_map_type>(const variant_type& var) {
176  if (var.which() == 0) {
177  const flexible_type& ft = variant_get_ref<flexible_type>(var);
178  if (ft.get_type() == flex_type_enum::DICT) {
179  // The argument is a flex_dict but we expected a variant_map_type. Attempt
180  // a conversion. Note that this will fail if any flex_dict keys are not
181  // strings, but we would have failed anyway in variant_get_value below.
182  variant_map_type ret;
183  for (const auto& kv : ft.get<flex_dict>()) {
184  ret[kv.first.get<flex_string>()] = to_variant(kv.second);
185  }
186  return ret;
187  }
188  }
189 
190  return variant_get_value<variant_map_type>(var);
191 }
192 
193 /**
194  * Fills in a boost::fusion::vector<T ...> with parameters from the
195  * toolkit invocation object.
196  *
197  * InArgType is a boost::fusion::vector<T ...> representing the input arguments
198  * of the user defined function.
199  *
200  * inargnames is a vector of strings naming each of the argument, and should
201  * preferably, (but not necessary) be of the same length as InArgType.
202  *
203  * Essentially, this performs the following simple task:
204  *
205  * inargs[n] = params[inargnames[n]]
206  *
207  * Except this cannot be done so easily because each entry in the
208  * boost fusion vector is of a different type. So a bit more work is needed.
209  *
210  * If inargnames is shorter than the actual number of arguments, only
211  * inargnames.size() number of arguments is filled. If it is longer, only the
212  * first inargs.size() names are taken.
213  */
214 template <typename InArgType>
216  InArgType* inargs; // pointer to the input arguments
217  std::vector<std::string> inargnames; // name of each input argument
218  variant_map_type* params; // pointer to the user's input which called the function
219 
220  template<int n>
221  void operator()(boost::mpl::integral_c<int, n> t) const {
222  // the type of inargs[n] the use of std::decay removes all references
223  typedef typename std::decay<decltype(boost::fusion::at_c<n>(*inargs))>::type element_type;
224  if (n < inargnames.size()) {
225  variant_map_type::const_iterator kv = params->find(inargnames[n]);
226  if (kv == params->end()) {
227  std_log_and_throw(std::invalid_argument,
228  "Missing toolkit function parameter: " + inargnames[n]);
229  }
230 
231  boost::fusion::at_c<n>(*inargs) = read_arg<element_type>(kv->second);
232  }
233  }
234 };
235 
236 
237 /**
238  * Fills in a boost::fusion::vector<T ...> with parameters from the
239  * std::vector<variant_type>.
240  *
241  * InArgType is a boost::fusion::vector<T ...> representing the input arguments
242  * of the user defined function.
243  *
244  * params is a vector of variants of the same length as InArgType.
245  *
246  * Essentially, this performs the following simple task:
247  *
248  * inargs[n] = params[n]
249  *
250  * Except this cannot be done so easily because each entry in the
251  * boost fusion vector is of a different type. So a bit more work is needed.
252  */
253 template <typename InArgType>
254 struct fill_in_args {
255  InArgType* inargs; // pointer to the input arguments
256  const std::vector<variant_type>* params; // pointer to the user's input which called the function
257 
258  template<int n>
259  void operator()(boost::mpl::integral_c<int, n> t) const {
260  // the type of inargs[n] the use of std::decay removes all references
261  typedef typename std::decay<decltype(boost::fusion::at_c<n>(*inargs))>::type element_type;
262  if (n < params->size()) {
263  boost::fusion::at_c<n>(*inargs) = variant_get_value<element_type>((*params)[n]);
264  }
265  }
266 };
267 
268 /**
269  * A type function that maps int --> range<int>(0, N).
270  *
271  * Given a number N, make_range<N>::type is a boost mpl range type representing
272  * a sequence from 0 to N exclusive.
273  *
274  */
275 template <size_t N>
276 struct make_range {
277  typedef typename boost::mpl::range_c<int, 0, N>::type type;
278 };
279 
280 
281 /**
282  * A type function that maps (int, int) --> range<int>(Start, End).
283  *
284  * Given two numbers Start, End , make_range<Start, End>::type is a boost mpl
285  * range type representing a sequence from Start to End exclusive.
286  *
287  */
288 template <size_t Start, size_t End>
289 struct make_range2 {
290  typedef typename boost::mpl::range_c<int, Start, End>::type type;
291 };
292 
293 
294 /**
295  * Wraps a function f(...) with a function that takes a variant_map_type
296  * and returns a variant_type.
297  * Essentially, given a function f of type ret(in1, in2, in3 ...),
298  * returns a function g of type variant_type(variant_map_type) where g
299  * performs the equivalent of:
300  *
301  * \code
302  * variant_type g(variant_map_type input) {
303  * return variant_encode( f (variant_decode(input[inargnames[0]),
304  * variant_decode(input[inargnames[1]),
305  * variant_decode(input[inargnames[2]),
306  * ...));
307  * }
308  *
309  * \endcode
310  */
311 template <size_t NumInArgs, typename Function>
312 std::function<variant_type(variant_map_type)>
313 generate_function_wrapper(Function fn, std::vector<std::string> inargnames) {
314  // import some mpl stuff we will be using
315  using boost::mpl::erase;
316  using boost::mpl::size;
317  using boost::mpl::int_;
318  using boost::mpl::_1;
319 
320  // the actual spec object we are returning
322  // Get a function traits object from the function type
323  typedef boost::function_traits<typename std::remove_pointer<Function>::type > fntraits;
324  // Get an mpl::vector containing all the arguments of the user defined function
325  typedef typename function_args_to_mpl_vector<Function>::type fn_args_type_original;
326  // decay it to element const, references, etc.
327  typedef typename boost::mpl::transform<fn_args_type_original, std::decay<_1>>::type fn_args_type;
328 
329  static_assert(size<fn_args_type>::value == NumInArgs,
330  "Invalid number arguments. #input != #function arguments.");
331  typedef fn_args_type in_arg_types;
332 
333  auto fnwrapper =
334  [fn, inargnames](variant_map_type args)->variant_type {
336  // we need to fill the input arguments and output arguments
337  typename boost::fusion::result_of::as_vector<in_arg_types>::type in_args;
338  /*
339  * Essentially:
340  *
341  * for i = 0 to #inargs - 1:
342  * inargs[i] = invoke->params[inargnames[i]]
343  *
344  * The fill_named_in_args struct performs the body of the for loop.
345  */
347  in_arg_filler.params = &args;
348  in_arg_filler.inargnames = inargnames;
349  in_arg_filler.inargs = &in_args;
350  typename make_range<size<decltype(in_args)>::value>::type in_arg_range;
351  boost::fusion::for_each(in_arg_range, in_arg_filler);
352 
353  // Invoke the function call, storing the return value
355  retval.call([&](){
356  return boost::fusion::invoke(fn, in_args);
357  });
358  if (retval.is_void) return to_variant(FLEX_UNDEFINED);
359  else return to_variant(retval.ret_value);
360  };
361  return fnwrapper;
362 }
363 
364 
365 /**
366  * Wraps a function f(...) with a function that takes a variant_map_type
367  * and returns a variant_type.
368  * Essentially, given a function f of type ret(in1, in2, in3 ...),
369  * returns a function g of type variant_type(variant_map_type) where g
370  * performs the equivalent of:
371  *
372  * \code
373  * variant_type g(variant_map_type input) {
374  * return variant_encode( f (variant_decode(input[inargnames[0]),
375  * variant_decode(input[inargnames[1]),
376  * variant_decode(input[inargnames[2]),
377  * ...));
378  * }
379  *
380  * \endcode
381  */
382 template <size_t NumInArgs, typename Function>
383 std::function<variant_type(const std::vector<variant_type>&)>
384 generate_native_function_wrapper(Function fn) {
385  // import some mpl stuff we will be using
386  using boost::mpl::erase;
387  using boost::mpl::size;
388  using boost::mpl::int_;
389  using boost::mpl::_1;
390 
391  // the actual spec object we are returning
393  // Get a function traits object from the function type
394  typedef boost::function_traits<typename std::remove_pointer<Function>::type > fntraits;
395  // Get an mpl::vector containing all the arguments of the user defined function
396  typedef typename function_args_to_mpl_vector<Function>::type fn_args_type_original;
397  // decay it to element const, references, etc.
398  typedef typename boost::mpl::transform<fn_args_type_original, std::decay<_1>>::type fn_args_type;
399 
400  static_assert(size<fn_args_type>::value == NumInArgs,
401  "Invalid number arguments. #input != #function arguments.");
402  typedef fn_args_type in_arg_types;
403 
404  auto fnwrapper =
405  [fn](const std::vector<variant_type>& args)->variant_type {
406  if (args.size() != fntraits::arity) {
407  throw std::string("Insufficient arguments");
408  }
409  // we need to fill the input arguments and output arguments
410  typename boost::fusion::result_of::as_vector<in_arg_types>::type in_args;
411  /*
412  * Essentially:
413  *
414  * for i = 0 to #inargs - 1:
415  * inargs[i] = invoke->params[i]
416  *
417  * The fill_in_args struct performs the body of the for loop.
418  */
419  fill_in_args<decltype(in_args)> in_arg_filler;
420  in_arg_filler.params = &args;
421  in_arg_filler.inargs = &in_args;
422  typename make_range<size<decltype(in_args)>::value>::type in_arg_range;
423  boost::fusion::for_each(in_arg_range, in_arg_filler);
424 
425  // Invoke the function call, storing the return value
427  retval.call([&](){
428  return boost::fusion::invoke(fn, in_args);
429  });
430  if (retval.is_void) return to_variant(FLEX_UNDEFINED);
431  else return to_variant(retval.ret_value);
432  };
433  return fnwrapper;
434 }
435 
436 
437 
438 
439 /**
440  * Wraps a member function T::f(...) with a function that takes a
441  * variant_map_type and returns a variant_type.
442  * Essentially, given a function f of type ret(in1, in2, in3 ...),
443  * returns a function g of type variant_type(variant_map_type) where g
444  * performs the equivalent of:
445  *
446  * \code
447  * variant_type g(T* t, variant_map_type input) {
448  * return variant_encode( t->f (variant_decode(input[inargnames[0]),
449  * variant_decode(input[inargnames[1]),
450  * variant_decode(input[inargnames[2]),
451  * ...));
452  * }
453  *
454  * \endcode
455  */
456 template <size_t NumInArgs, typename T, typename Ret, typename... Args>
457 std::function<variant_type(T*, variant_map_type)>
458 generate_member_function_wrapper(Ret (T::* fn)(Args...),
459  std::vector<std::string> inargnames) {
460  // import some mpl stuff we will be using
461  using boost::mpl::erase;
462  using boost::mpl::size;
463  using boost::mpl::int_;
464  using boost::mpl::_1;
465 
466  // the actual spec object we are returning
468  // convert Ret (T::* fn)(Args...) to Ret fn(T*, Args...)
469  auto member_fn = std::mem_fn(fn);
470  // Get an mpl::vector containing all the arguments of the member function
471  typedef typename boost::mpl::vector<T*, Args...>::type fn_args_type_original;
472  // decay it to element const, references, etc.
473  typedef typename boost::mpl::transform<fn_args_type_original, std::decay<_1>>::type fn_args_type;
474 
475  static_assert(size<fn_args_type>::value == NumInArgs + 1,
476  "Invalid number arguments. #input != #function arguments.");
477  typedef fn_args_type in_arg_types;
478  // pad inargnames in front with one more element to cover for the "this" argument
479  inargnames.insert(inargnames.begin(), "");
480 
481  auto fnwrapper =
482  [member_fn, inargnames](T* t, variant_map_type args)->variant_type {
484  // we need to fill the input arguments and output arguments
485  typename boost::fusion::result_of::as_vector<in_arg_types>::type in_args;
486  /*
487  * Essentially:
488  * inargs[0] = t
489  * for i = 1 to #inargs - 1:
490  * inargs[i] = invoke->params[inargnames[i]]
491  *
492  * The fill_named_in_args struct performs the body of the for loop.
493  */
494  boost::fusion::at_c<0>(in_args) = t;
496  in_arg_filler.params = &args;
497  in_arg_filler.inargnames = inargnames;
498  in_arg_filler.inargs = &in_args;
499  typename make_range2<1, size<decltype(in_args)>::value>::type in_arg_range;
500  boost::fusion::for_each(in_arg_range, in_arg_filler);
501 
502  // Invoke the function call, storing the return value
504  retval.call([&](){
505  return boost::fusion::invoke(member_fn, in_args);
506  });
507  if (retval.is_void) return to_variant(FLEX_UNDEFINED);
508  else return to_variant(retval.ret_value);
509  };
510  return fnwrapper;
511 }
512 
513 
514 /**
515  * Wraps a const member function T::f(...) const with a function that takes a
516  * variant_map_type and returns a variant_type.
517  * Essentially, given a function f of type ret(in1, in2, in3 ...),
518  * returns a function g of type variant_type(variant_map_type) where g
519  * performs the equivalent of:
520  *
521  * \code
522  * variant_type g(T* t, variant_map_type input) {
523  * return variant_encode( t->f (variant_decode(input[inargnames[0]),
524  * variant_decode(input[inargnames[1]),
525  * variant_decode(input[inargnames[2]),
526  * ...));
527  * }
528  *
529  * \endcode
530  *
531  * \note Annoyingly we need a seperate specialization for this even
532  * though the code is... *identical*. I can't figure out how to templatize
533  * around the const.
534  */
535 template <size_t NumInArgs, typename T, typename Ret, typename... Args>
536 std::function<variant_type(T*, variant_map_type)>
537 generate_const_member_function_wrapper(Ret (T::* fn)(Args...) const,
538  std::vector<std::string> inargnames) {
539  // import some mpl stuff we will be using
540  using boost::mpl::erase;
541  using boost::mpl::size;
542  using boost::mpl::int_;
543  using boost::mpl::_1;
544 
545  // the actual spec object we are returning
547  // convert Ret (T::* fn)(Args...) to Ret fn(T*, Args...)
548  auto member_fn = std::mem_fn(fn);
549  // Get an mpl::vector containing all the arguments of the member function
550  typedef typename boost::mpl::vector<T*, Args...>::type fn_args_type_original;
551  // decay it to element const, references, etc.
552  typedef typename boost::mpl::transform<fn_args_type_original, std::decay<_1>>::type fn_args_type;
553 
554  static_assert(size<fn_args_type>::value == NumInArgs + 1,
555  "Invalid number arguments. #input != #function arguments.");
556  typedef fn_args_type in_arg_types;
557  // pad inargnames in front with one more element to cover for the "this" argument
558  inargnames.insert(inargnames.begin(), "");
559 
560  auto fnwrapper =
561  [member_fn, inargnames](T* t, variant_map_type args)->variant_type {
563  // we need to fill the input arguments and output arguments
564  typename boost::fusion::result_of::as_vector<in_arg_types>::type in_args;
565  /*
566  * Essentially:
567  * inargs[0] = t
568  * for i = 1 to #inargs - 1:
569  * inargs[i] = invoke->params[inargnames[i]]
570  *
571  * The fill_named_in_args struct performs the body of the for loop.
572  */
573  boost::fusion::at_c<0>(in_args) = t;
575  in_arg_filler.params = &args;
576  in_arg_filler.inargnames = inargnames;
577  in_arg_filler.inargs = &in_args;
578  typename make_range2<1, size<decltype(in_args)>::value>::type in_arg_range;
579  boost::fusion::for_each(in_arg_range, in_arg_filler);
580 
581  // Invoke the function call, storing the return value
583  retval.call([&](){
584  return boost::fusion::invoke(member_fn, in_args);
585  });
586  if (retval.is_void) return to_variant(FLEX_UNDEFINED);
587  else return to_variant(retval.ret_value);
588  };
589  return fnwrapper;
590 }
591 
592 
593 /**
594  * Generates a toolkit specification object for a user defined function which
595  * wraps the user defined function with a helper that provides type checking,
596  * argument filling and exception handling.
597  *
598  * The basic model of toolkit function publishing is that the user
599  * define a function of the form:
600  * \code
601  * toolkit_function_response_type user_function(toolkit_function_invocation& invoke)
602  * \endcode
603  * Where toolkit_function_invocation essentially stores a dictionary of input arguments,
604  * and toolkit_function_response_type stores a dictionary of outputs.
605  *
606  * However, this can be quite difficult to use in practice, especially due to
607  * dynamic typing which requires a lot of additional typechecking and validation
608  * overhead.
609  *
610  * The basic idea behind this function is to allow the user can publish
611  * arbitrary functions of the form:
612  *
613  * \code
614  * return_type function_name(InArg1 arg1, InArg2 arg2...)
615  * \endcode
616  *
617  * Note that all output argument must appear AFTER all the input arguments.
618  *
619  * Input argument types are one of:
620  * - flexible_type
621  * - unity_sarray*
622  * - unity_sframe*
623  * - unity_sgraph*
624  * - any type contained by flexible_type: i.e. flex_int, flex_vec, etc.
625  *
626  * The return type can similarly be any input argument type, or any type which
627  * can be converted into a flexible_type.
628  *
629  * For instance
630  * \code
631  * size_t demo(flex_int arg1, unity_sarray* arg2) {
632  * ...
633  * }
634  * \endcode
635  *
636  * Then to publish it:
637  * \code
638  * toolkit_function_specification spec =
639  * make_spec<2>(demo, "demo", {"arg1name", "arg2name"});
640  * \endcode
641  *
642  * Will return a toolkit specification object that publishes the user defined
643  * function to Python under the name "fnname". And mapping the input
644  * dictionary to the input arguments of the function using "inargnames"
645  * and mapping output arguments to the output dictionary using outargnames.
646  *
647  * Essentially, with reference to the example demo function above, a helper
648  * function is produced which performs the following, but with more error
649  * checking and validation.
650  * \code
651  * toolkit_function_response_type helper(toolkit_function_invocation& invoke) {
652  * // perform the call
653  * flex_int arg1 = invoke.params["arg1name"];
654  * unity_sarray* arg2 = invoke.params["arg2name"];
655  * auto result = demo(arg1, arg2);
656  *
657  * // generate response
658  * toolkit_function_response_type ret;
659  * ret.params["return_value"] = result;
660  * ret.success = true;
661  * return ret;
662  * }
663  * \endcode
664  * This helper is then published under the name specified in fnname, and will be
665  * callable from python via
666  * \code
667  * import turicreate.toolkits.main as main
668  * ret = main.run("demo", {'arg1name':5, 'arg2name':array})
669  * \endcode
670  *
671  *
672  * \tparam NumInArgs The number of input arguments of the user function
673  * \tparam Function The type of the function. Usually does not need to be
674  * specified. This should be automatically inferable.
675  *
676  * \param fn Pointer to the user function
677  * \param fnname The published name of the function. (what Python sees)
678  * \param inargnames The published input argument names (what Python sees)
679  */
680 template <size_t NumInArgs, typename Function>
681 toolkit_function_specification make_spec(Function fn, std::string fnname,
682  std::vector<std::string> inargnames) {
683  // the actual spec object we are returning
685  auto fnwrapper = generate_function_wrapper<NumInArgs, Function>(fn, inargnames);
686  auto native_fn_wrapper = generate_native_function_wrapper<NumInArgs, Function>(fn);
687 
688  auto invoke_fn = [fnwrapper,inargnames](turi::toolkit_function_invocation& invoke)->turi::toolkit_function_response_type {
690  // we are inside the actual toolkit call now.
691  try {
692  variant_type val = fnwrapper(invoke.params);
693  ret.params["return_value"] = val;
694  ret.success = true;
695  // final bit of error handling in the case of exceptions
696  } catch (std::string err) {
697  ret.message = err;
698  ret.success = false;
699  } catch (const char* err) {
700  ret.message = err;
701  ret.success = false;
702  } catch (std::exception& e) {
703  ret.message = e.what();
704  ret.success = false;
705  } catch (...) {
706  ret.message = "Unknown Exception";
707  ret.success = false;
708  }
709  return ret;
710  };
711 
712  // complete the function registration.
713  // get the python exposed function name. Remove any namespacing is any.
714  auto last_colon = fnname.find_last_of(":");
715  if (last_colon == std::string::npos) spec.name = fnname;
716  else spec.name = fnname.substr(last_colon + 1);
717 
718  // store the function
719  spec.toolkit_execute_function = invoke_fn;
720  spec.native_execute_function = native_fn_wrapper;
721  spec.description["arguments"] =
723  spec.description["_raw_fn_pointer_"] = reinterpret_cast<size_t>(fn);
724 
725  return spec;
726 }
727 
728 
729 
730 /**
731  * Generates a toolkit specification object for a user defined function which
732  * wraps the user defined function with a helper that provides type checking,
733  * argument filling and exception handling.
734  *
735  * Performs exactly the same thing as make_spec, but takes the final inargnames
736  * argument as a variable length argument.
737  */
738 template <typename Function, typename... Args>
739 toolkit_function_specification make_spec_indirect(Function fn, std::string fnname,
740  Args... args) {
741  // Get a function traits object from the function type
742  typedef boost::function_traits<typename std::remove_pointer<Function>::type> fntraits;
743  static_assert(fntraits::arity == sizeof...(Args),
744  "Incorrect number input parameter names specified.");
745  return make_spec<sizeof...(Args)>(fn, fnname, {args...});
746 }
747 
748 
749 
750 } // toolkit_function_wrapper_impl
751 } // turicreate
752 
753 #endif
std::function< variant_type(const std::vector< variant_type > &invoke)> native_execute_function
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
std::map< std::string, flexible_type > description
const T & get() const
flex_type_enum get_type() const
std::string flex_string
variant_type to_variant(const T &f)
Definition: variant.hpp:308
std::function< toolkit_function_response_type(toolkit_function_invocation &)> toolkit_execute_function
std::vector< std::pair< flexible_type, flexible_type > > flex_dict
static flexible_type FLEX_UNDEFINED