Turi Create  4.0
toolkit_class_macros.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_TOOLKIT_CLASS_MACROS_HPP
7 #define TURI_UNITY_TOOLKIT_CLASS_MACROS_HPP
8 #include <string>
9 #include <model_server/lib/toolkit_util.hpp>
10 #include <model_server/lib/toolkit_function_specification.hpp>
11 #include <model_server/lib/toolkit_class_specification.hpp>
12 #include <model_server/lib/toolkit_function_wrapper_impl.hpp>
13 #include <model_server/lib/toolkit_class_wrapper_impl.hpp>
14 #include <model_server/lib/extensions/model_base.hpp>
15 
16 /**
17  * \defgroup group_gl_class_ffi Class Extension Interface
18  * \ingroup group_gl_ffi
19  *
20  * The class Extension Interface provides a collection of macros that automate the
21  * process of exporting a class to Python. The macros are located in
22  * sdk/toolkit_class_macros.hpp.
23  *
24  * For detailed usage descriptions, see page_turicreate_extension_interface .
25  *
26  * Example:
27  * \code
28  * // class must inherit from model_base
29  * #include <model_server/lib/toolkit_class_macros.hpp>
30  * using namespace turi;
31  *
32  * class example: public model_base {
33  * std::string hello_world() {
34  * return "hello world";
35  * }
36  *
37  * std::string concat(std::string a, std::string b) {
38  * return a + b;
39  * }
40  * // register class members.
41  * BEGIN_CLASS_MEMBER_REGISTRATION("example")
42  * REGISTER_CLASS_MEMBER_FUNCTION(example::hello_world);
43  * REGISTER_CLASS_MEMBER_FUNCTION(example::concat, "a", "b");
44  * END_CLASS_MEMBER_REGISTRATION
45  * };
46  *
47  * // register class
48  * BEGIN_CLASS_REGISTRATION
49  * REGISTER_CLASS(example)
50  * END_CLASS_REGISTRATION
51  * \endcode
52  *
53  * \{
54  */
55 
56 /**
57  * Begins a class member registration block.
58  *
59  * BEGIN_CLASS_MEMBER_REGISTRATION(python_facing_classname)
60  *
61  * The basic usage is to put this inside a class to be published, and go:
62  * \code
63  * BEGIN_CLASS_MEMBER_REGISTRATION("python_facing_classname")
64  * REGISTER_ ... OTHER MEMBERS ...
65  * END_CLASS_MEMBER_REGISTRATION
66  * \endcode
67  */
68 #define BEGIN_CLASS_MEMBER_REGISTRATION(python_facing_classname) \
69  public: \
70  virtual inline const char* name() override { return python_facing_classname; } \
71  virtual inline const std::string& uid() override { \
72  static std::string _uid = ("__LINE__," __FILE__); \
73  return _uid; \
74  } \
75  virtual inline void perform_registration() override { \
76  if (is_registered()) return;
77 
78 /**
79  * Registers a single class member function.
80  *
81  * REGISTER_CLASS_MEMBER_FUNCTION(function, ...var args of input argument names ...)
82  *
83  * Registers a function with no arguments.
84  * REGISTER_CLASS_MEMBER_FUNCTION(class::function)
85  *
86  * Registers a function with 2 input arguments. The first input argument is
87  * named "a" and the 2nd input argument is named "b"
88  * REGISTER_CLASS_MEMBER_FUNCTION(class::function, "a", "b")
89  *
90  * Example:
91  *
92  * \code
93  * class example: public model_base {
94  * std::string hello_world() {
95  * return "hello world";
96  * }
97  *
98  * std::string concat(std::string a, std::string b) {
99  * return a + b;
100  * }
101  *
102  * BEGIN_CLASS_MEMBER_REGISTRATION("example")
103  * REGISTER_CLASS_MEMBER_FUNCTION(example::hello_world);
104  * REGISTER_CLASS_MEMBER_FUNCTION(example::concat, "a", "b");
105  * END_CLASS_MEMBER_REGISTRATION
106  * }
107  * \endcode
108  *
109  * The return value of the function will be returned to Python. The function
110  * can return void. If the function fails, it should throw an exception which
111  * will be forward back to Python as RuntimeError.
112  */
113 #define REGISTER_CLASS_MEMBER_FUNCTION(function, ...) \
114  register_function(#function, \
115  std::vector<std::string>{__VA_ARGS__}, \
116  toolkit_class_wrapper_impl::generate_member_function_wrapper_indirect( \
117  &function, ##__VA_ARGS__));
118 
119 /**
120  * Like REGISTER_CLASS_MEMBER_FUNCTION but allows the python-facing name of
121  * the function to be redefined.
122  *
123  * REGISTER_NAMED_CLASS_MEMBER_FUNCTION(python_name, function,
124  * ...var args of input argument names ...)
125  *
126  * Registers a function with 2 input arguments. The function shall be called
127  * "hello" in Python. The first input argument is named "a" and the 2nd input
128  * argument is named "b".
129  *
130  * REGISTER_NAMED_CLASS_MEMBER_FUNCTION("hello", class::function, "a", "b")
131  */
132 #define REGISTER_NAMED_CLASS_MEMBER_FUNCTION(name, function, ...) \
133  register_function(name, \
134  std::vector<std::string>{__VA_ARGS__}, \
135  toolkit_class_wrapper_impl::generate_member_function_wrapper_indirect( \
136  &function, ##__VA_ARGS__));
137 
138 /**
139  * Begins a class member registration block for base classes. These
140  *
141  * BEGIN_BASE_CLASS_MEMBER_REGISTRATION()
142  *
143  * The basic usage is to put this inside a class to be published, and go:
144  * \code
145  * BEGIN_BASE_CLASS_MEMBER_REGISTRATION("python_facing_classname")
146  * REGISTER_ ... OTHER MEMBERS ...
147  * END_CLASS_MEMBER_REGISTRATION
148  * \endcode
149  *
150  * In the parent class, you would register
151  *
152  *
153  */
154 #define BEGIN_BASE_CLASS_MEMBER_REGISTRATION() \
155  public: \
156  virtual inline void perform_registration() override { \
157 
158 
159 /**
160  * Begins a class member registration block for a base class.
161  *
162  * BEGIN_BASE_CLASS_MEMBER_REGISTRATION()
163  *
164  * The basic usage is to put this inside a class that is the base class of an inherited, and go:
165  * \code
166  * BEGIN_BASE_CLASS_MEMBER_REGISTRATION()
167  * REGISTER_ ... OTHER MEMBERS ...
168  * END_CLASS_MEMBER_REGISTRATION
169  * \endcode
170  */
171 #define IMPORT_BASE_CLASS_REGISTRATION(base_class) \
172  static_assert( \
173  !std::is_same<decltype(*this), base_class>::value, \
174  "IMPORT_BASE_CLASS_REGISTRATION must be called on base class."); \
175  this->base_class::perform_registration();
176 
177 // /*
178 // * Like REGISTER_CLASS_MEMBER_FUNCTION but to be used when the function to
179 // * be exposed is overloaded.
180 // *
181 // * REGISTER_OVERLOADED_CLASS_MEMBER_FUNCTION(function_type, function,
182 // * ...var args of input argument names ...)
183 // *
184 // * When a function is overloaded, the function pointer classname::function is
185 // * ill-defined and one particular type of the function has to be selected.
186 // *
187 // * Example:
188 // * \code
189 // * class example: public model_base {
190 // *
191 // * std::string concat(std::string a, std::string b) {
192 // * return a + b;
193 // * }
194 // * std::string concat(std::string a, std::string b, std::string c) {
195 // * return a + b + c;
196 // * }
197 // *
198 // * BEGIN_CLASS_MEMBER_REGISTRATION("example")
199 // * // the type of the concat function we want
200 // * typedef std::string (example::* two_arg_concat_type)(std::string ,std::string);
201 // * REGISTER_OVERLOADED_CLASS_MEMBER_FUNCTION(two_arg_concat_type, example::concat, "a", "b");
202 // * END_CLASS_MEMBER_REGISTRATION
203 // * }
204 // * \endcode
205 // */
206 /* #define REGISTER_OVERLOADED_CLASS_MEMBER_FUNCTION(overload_type, function, ...) \
207  * register_function(#function, \
208  * std::vector<std::string>{__VA_ARGS__}, \
209  * toolkit_class_wrapper_impl::generate_member_function_wrapper_indirect( \
210  * static_cast<overload_type>(&function), ##__VA_ARGS__));
211  */
212 // /*
213 // * Like REGISTER_CLASS_MEMBER_FUNCTION but to be used when the function to
214 // * be exposed is overloaded, AND the python facing function is to be redefined.
215 // *
216 // * REGISTER_NAMED_OVERLOADED_CLASS_MEMBER_FUNCTION(python_name,
217 // * function_type, function,
218 // * ...var args of input argument names ...)
219 // *
220 // * When a function is overloaded, the function pointer classname::function is
221 // * ill-defined and one particular type of the function has to be selected.
222 // *
223 // * Example:
224 // * \code
225 // * class example: public model_base {
226 // *
227 // * std::string concat(std::string a, std::string b) {
228 // * return a + b;
229 // * }
230 // * std::string concat(std::string a, std::string b, std::string c) {
231 // * return a + b + c;
232 // * }
233 // *
234 // * BEGIN_CLASS_MEMBER_REGISTRATION("example")
235 // * // the type of the concat function we want
236 // * typedef std::string (example::* two_arg_concat_type)(std::string ,std::string);
237 // * REGISTER_OVERLOADED_CLASS_MEMBER_FUNCTION("concat_two", two_arg_concat_type,
238 // * example::concat, "a", "b");
239 // * typedef std::string (example::* three_arg_concat_type)(std::string ,std::string);
240 // * REGISTER_OVERLOADED_CLASS_MEMBER_FUNCTION("concat_three", three_arg_concat_type,
241 // * example::concat, "a", "b");
242 // * END_CLASS_MEMBER_REGISTRATION
243 // * }
244 // * \endcode
245 // */
246 /* #define REGISTER_OVERLOADED_NAMED_CLASS_MEMBER_FUNCTION(name, overload_type, function, ...) \
247  * register_function(name, \
248  * std::vector<std::string>{__VA_ARGS__}, \
249  * toolkit_class_wrapper_impl::generate_member_function_wrapper_indirect( \
250  * static_cast<overload_type>(&function), ##__VA_ARGS__));
251  */
252 
253 /// \internal
255  /*
256  * Registers a docstring for a function name. This in combination with
257  * the other overload for register_docstring allows for the macro
258  * \code
259  * register_docstring(name, #name, docstring)
260  * \endcode
261  * And that will work regardless of whether name is a string, or symbol.
262  */
263  inline std::pair<std::string, std::string>
264  get_docstring(const char** fnname,
265  std::string __unused__,
266  std::string docstring) {
267  return {*fnname, docstring};
268  }
269 
270  template <typename T>
271  inline std::pair<std::string, std::string>
272  get_docstring(T t,
273  std::string fnname,
274  std::string docstring) {
275  return {fnname, docstring};
276  }
277 } // end namespacedocstring_macro_impl
278 
279 /**
280  * Registers a docstring of a function or property previously registered with
281  * any of the registration functions.
282  *
283  * Name can be a function, or a string.
284  * (Generally for the name, you put the first argument of any of
285  * the REGISTER macros and it should work fine)
286  *
287  * \code
288  * BEGIN_CLASS_MEMBER_REGISTRATION("example")
289  * REGISTER_CLASS_MEMBER_FUNCTION(example::hello_world);
290  * REGISTER_CLASS_MEMBER_DOCSTRING(example::hello_world, "prints hello world")
291  *
292  * REGISTER_NAMED_CLASS_MEMBER_FUNCTION("hello", example::say_hello, "a", "b")
293  * REGISTER_CLASS_MEMBER_DOCSTRING("hello", "says hello")
294  *
295  * REGISTER_PROPERTY(abc)
296  * REGISTER_CLASS_MEMBER_DOCSTRING(abc, "contains the abc property")
297  * REGISTER_CLASS_DOCSTRING("example class")
298  * END_CLASS_MEMBER_REGISTRATION
299  * \endcode
300  */
301 #define REGISTER_CLASS_MEMBER_DOCSTRING(name, docstring) \
302  this->register_docstring(docstring_macro_impl::get_docstring(&name, #name, docstring));
303 
304 /**
305  * Registers a docstring of a class
306  * \code
307  * BEGIN_CLASS_MEMBER_REGISTRATION("example")
308  * REGISTER_CLASS_MEMBER_FUNCTION(example::hello_world);
309  * REGISTER_CLASS_MEMBER_DOCSTRING(example::hello_world, "prints hello world")
310  *
311  * REGISTER_NAMED_CLASS_MEMBER_FUNCTION("hello", example::say_hello, "a", "b")
312  * REGISTER_CLASS_MEMBER_DOCSTRING("hello", "says hello")
313  *
314  * REGISTER_PROPERTY(abc)
315  * REGISTER_CLASS_MEMBER_DOCSTRING(abc, "contains the abc property")
316  * REGISTER_CLASS_DOCSTRING("example class")
317  * END_CLASS_MEMBER_REGISTRATION
318  * \endcode
319  */
320 #define REGISTER_CLASS_DOCSTRING(docstring) \
321  this->register_docstring({"__doc__", docstring});
322 
323 /**
324  * Registers a function as a getter for a python property.
325  *
326  * REGISTER_GETTER(python_property_name, getter_function)
327  *
328  * The getter_function must return a single value and take no arguments.
329  *
330  * \code
331  * class example: public model_base {
332  *
333  * std::string get_value() {
334  * return value;
335  * }
336  * void set_value(std::string a) {
337  * a = value;
338  * }
339  * std::string value;
340  *
341  * BEGIN_CLASS_MEMBER_REGISTRATION("example")
342  * REGISTER_GETTER("value", example::get_value)
343  * REGISTER_SETTER("value", example::set_value)
344  * END_CLASS_MEMBER_REGISTRATION
345  * \endcode
346  */
347 #define REGISTER_GETTER(propname, function) \
348  register_getter(propname, \
349  toolkit_class_wrapper_impl::generate_getter( \
350  &function));
351 
352 /**
353  * Registers a function as a setter for a python property.
354  *
355  * REGISTER_SETTER(python_property_name, setter_function)
356  *
357  * The setter_function must have a single argument, and return no values.
358  *
359  * \code
360  * class example: public model_base {
361  *
362  * std::string get_value() {
363  * return value;
364  * }
365  * void set_value(std::string a) {
366  * a = value;
367  * }
368  * std::string value;
369  *
370  * BEGIN_CLASS_MEMBER_REGISTRATION("example")
371  * REGISTER_GETTER("value", example::get_value)
372  * REGISTER_SETTER("value", example::set_value)
373  * END_CLASS_MEMBER_REGISTRATION
374  * \endcode
375  */
376 #define REGISTER_SETTER(propname, function) \
377  register_setter(propname, \
378  toolkit_class_wrapper_impl::generate_setter( \
379  &function, "value"));
380 
381 /**
382  * Registers a member variable, automatically generating a getter and a setter.
383  *
384  * REGISTER_PROPERTY(member_variable)
385  *
386  * \code
387  * class example: public model_base {
388  * std::string value;
389  *
390  * BEGIN_CLASS_MEMBER_REGISTRATION("example")
391  * REGISTER_PROPERTY(value)
392  * END_CLASS_MEMBER_REGISTRATION
393  * \endcode
394  *
395  */
396 #define REGISTER_PROPERTY(propname) \
397  register_getter(#propname, [=](model_base* curthis, variant_map_type)->variant_type { \
398  return to_variant((dynamic_cast<std::decay<decltype(this)>::type>(curthis))->propname); \
399  }); \
400  register_setter(#propname, [=](model_base* curthis, variant_map_type in)->variant_type { \
401  (dynamic_cast<std::decay<decltype(this)>::type>(curthis))->propname = \
402  variant_get_value<decltype(propname)>(in["value"]); \
403  return to_variant(0); \
404  });
405 
406 /**
407  * Begins a class registration block.
408  * Basic usage:
409  *
410  * \code
411  * BEGIN_CLASS_REGISTRATION
412  * REGISTER_CLASS(example)
413  * END_CLASS_REGISTRATION
414  * \endcode
415  *
416  */
417 #define BEGIN_CLASS_REGISTRATION \
418  __attribute__((visibility("default"))) std::vector<::turi::toolkit_class_specification> get_toolkit_class_registration() { \
419  std::vector<::turi::toolkit_class_specification> specs;
420 
421 
422 
423 /**
424  * Ends a class member registration block.
425  * See BEGIN_CLASS_MEMBER_REGISTRATION
426  */
427 #define END_CLASS_MEMBER_REGISTRATION \
428  set_registered(); }
429 
430 
431 /**
432  * Begins a class registration block.
433  *
434  * Basic usage:
435  * \code
436  * BEGIN_CLASS_REGISTRATION
437  * REGISTER_CLASS(example)
438  * END_CLASS_REGISTRATION
439  * \endcode
440  *
441  */
442 #define REGISTER_CLASS(class_name) \
443  { \
444  ::turi::toolkit_class_specification spec; \
445  class_name c; \
446  spec.name = c.name(); \
447  spec.constructor = +[]() { return (::turi::model_base*)(new class_name); }; \
448  spec.description["functions"] = \
449  flexible_type_converter<std::map<std::string, \
450  std::vector<std::string>>>().set(c.list_functions()); \
451  spec.description["get_properties"] = \
452  flexible_type_converter<std::vector<std::string>>().set(c.list_get_properties()); \
453  spec.description["set_properties"] = \
454  flexible_type_converter<std::vector<std::string>>().set(c.list_set_properties()); \
455  spec.description["uid"] = \
456  flexible_type_converter<std::string>().set(c.uid()); \
457  specs.push_back(spec); \
458  }
459 /**
460  * Ends a class registration block.
461  *
462  * Basic usage:
463  * \code
464  * BEGIN_CLASS_REGISTRATION
465  * REGISTER_CLASS(example)
466  * END_CLASS_REGISTRATION
467  * \endcode
468  *
469  */
470 #define END_CLASS_REGISTRATION \
471  return specs; \
472  }
473 
474 /// \}
475 #endif // TURI_UNITY_TOOLKIT_MAGIC_MACROS_HPP