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
254
namespace
docstring_macro_impl
{
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
docstring_macro_impl
Definition:
toolkit_class_macros.hpp:254
model_server
lib
toolkit_class_macros.hpp
Generated by
1.8.13