MIRA
ReflectorWrapper.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) by
3  * MetraLabs GmbH (MLAB), GERMANY
4  * and
5  * Neuroinformatics and Cognitive Robotics Labs (NICR) at TU Ilmenau, GERMANY
6  * All rights reserved.
7  *
8  * Redistribution and modification of this code is strictly prohibited.
9  *
10  * IN NO EVENT SHALL "MLAB" OR "NICR" BE LIABLE TO ANY PARTY FOR DIRECT,
11  * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
12  * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF "MLAB" OR
13  * "NICR" HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14  *
15  * "MLAB" AND "NICR" SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
16  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
18  * ON AN "AS IS" BASIS, AND "MLAB" AND "NICR" HAVE NO OBLIGATION TO
19  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR MODIFICATIONS.
20  */
21 
22 /*
23  * @file ReflectorWrapper.h
24  * Python wrapper for MIRA reflectors.
25  *
26  * @author Christian Vollmer, Tim Langner
27  * @date: 2011/03/09
28  */
29 
30 #ifndef _MIRA_TOOLBOXES_PYTHON_REFLECTORWRAPPER_H_
31 #define _MIRA_TOOLBOXES_PYTHON_REFLECTORWRAPPER_H_
32 
33 #include <Python.h>
34 
36 
37 #ifndef Q_MOC_RUN
38 #include <boost/python/stl_iterator.hpp>
39 #endif
40 
41 #include <boost/preprocessor/arithmetic/div.hpp>
42 #include <boost/preprocessor/arithmetic/mul.hpp>
43 
46 #include <serialization/adapters/std/vector>
47 #include <python/PythonException.h>
48 #include <python/RPCDefinitions.h>
49 #include <python/ScopedGIL.h>
50 #include <python/Version.h>
51 
52 namespace mira { namespace python {
53 
55 
56 template <typename T>
57 inline std::vector<T> listToVector(const boost::python::object& l)
58 {
59  return std::vector<T>(boost::python::stl_input_iterator< T >(l),
60  boost::python::stl_input_iterator< T >());
61 }
62 
63 template<typename Reflector>
65 {
68  boost::python::object callerType)
69  {
70  return version;
71  }
72 };
73 
74 // expose protected MetaSerializer::version(VersionType, string) by subclassing
75 template<>
77 {
81  boost::python::object callerType)
82  {
83  PyObject* module_ = PyObject_GetAttrString(callerType.ptr(), "__module__");
84  PyObject* name_ = PyObject_GetAttrString(callerType.ptr(), "__name__");
85  std::string module = boost::python::extract<std::string>(module_);
86  std::string name = boost::python::extract<std::string>(name_);
87  return ((Derived&)r).version(version, module + "." + name);
88  }
89 };
90 
91 template<typename ReturnType = json::Value>
93 {
94  #define REFLECTORWRAPPERGEN_SERVICE_METHODS(z,n,_) \
95  static ReturnType rpcCall##n(boost::python::object callable BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, const json::Value& p)) { \
96  using namespace boost::python; \
97  ScopedGILLock lock; \
98  try \
99  { \
100  boost::python::object value = callable(BOOST_PP_ENUM_PARAMS_Z(z, n, p)); \
101  return boost::python::extract<ReturnType>(value); \
102  } \
103  catch(boost::python::error_already_set&) \
104  { \
105  MIRA_THROW(XInvalidParameter, "Error calling rpc method: " << getLastExceptionString()); \
106  } \
107  return ReturnType(); \
108  }
109  BOOST_PP_REPEAT(BOOST_PP_INC(RPC_METHODS_MAX_PARAMS), REFLECTORWRAPPERGEN_SERVICE_METHODS, nil)
110  #undef REFLECTORWRAPPERGEN_SERVICE_METHODS
111 };
112 
113 template<>
115 {
116  #define REFLECTORWRAPPERGEN_SERVICE_METHODS(z,n,_) \
117  static void rpcCall##n(boost::python::object callable BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, const json::Value& p)) { \
118  using namespace boost::python; \
119  ScopedGILLock lock; \
120  try \
121  { \
122  callable(BOOST_PP_ENUM_PARAMS_Z(z, n, p)); \
123  } \
124  catch(boost::python::error_already_set&) \
125  { \
126  MIRA_THROW(XInvalidParameter, "Error calling rpc method: " << getLastExceptionString()); \
127  } \
128  }
129  BOOST_PP_REPEAT(BOOST_PP_INC(RPC_METHODS_MAX_PARAMS), REFLECTORWRAPPERGEN_SERVICE_METHODS, nil)
130  #undef REFLECTORWRAPPERGEN_SERVICE_METHODS
131 };
132 
134 {
135 public:
136 
138 
139  // Since boost::python does not allow us to make this function pure virtual,
140  // we provide a dummy default implementation here.
141  virtual void member(const std::string& name,
142  boost::python::object getterCallable,
143  boost::python::object setterCallable)
144  {
145  MIRA_THROW(XRuntime, "This method should never be called. Something is horribly wrong!")
146  }
147 
148  virtual void property(const std::string& name,
149  boost::python::object getterCallable,
150  boost::python::object setterCallable)
151  {
152  MIRA_THROW(XRuntime, "This method should never be called. Something is horribly wrong!")
153  }
154 
155  virtual void interface(const std::string& name)
156  {
157  MIRA_THROW(XRuntime, "This method should never be called. Something is horribly wrong!")
158  }
159 
160  virtual void method(const std::string& name,
161  boost::python::object callable)
162  {
163  MIRA_THROW(XRuntime, "This method should never be called. Something is horribly wrong!")
164  }
165 };
166 
167 template<typename Reflector>
169 {
170 public:
171 
172  ReflectorWrapper(Reflector& r) :
173  mReflector(r)
174  {
175  }
176 
178  {
179  }
180 
181  void reflectCall(const std::string& context, boost::python::object callable)
182  {
183  if (!PyCallable_Check(callable.ptr()))
184  MIRA_THROW(XRuntime, "parameter is no callable object.");
185 
186  ScopedGILLock lock;
187  MIRA_REFLECT_CALL(Reflector, mReflector, context.c_str(), callable(*this));
188  }
189 
191  boost::python::object callerType)
192  {
193  return ReflectorWrapperVersionHelper<Reflector>::versionHelp(mReflector, version, callerType);
194  }
195 
196 #define REFLECTOR_WRAPPER_CALL_member(access, T) \
197  if (defaultValue.is_none())\
198  {\
199  mReflector.access(name.c_str(),\
200  Getter<T>(boost::bind(&ReflectorWrapper<Reflector>::get<T>, getterCallable)),\
201  Setter<T>(boost::bind(&ReflectorWrapper<Reflector>::set<T>, setterCallable, _1)),\
202  comment.c_str());\
203  }\
204  else\
205  {\
206  mReflector.access(name.c_str(),\
207  Getter<T>(boost::bind(&ReflectorWrapper<Reflector>::get<T>, getterCallable)),\
208  Setter<T>(boost::bind(&ReflectorWrapper<Reflector>::set<T>, setterCallable, _1)),\
209  comment.c_str(),\
210  extract<T>(defaultValue));\
211  }
212 
213 #define REFLECTOR_WRAPPER_CALL_roproperty(access, T) \
214  mReflector.access(name.c_str(),\
215  Getter<T>(boost::bind(&ReflectorWrapper<Reflector>::get<T>, getterCallable)),\
216  comment.c_str());
217 
218 #define REFLECTOR_WRAPPER_CALL_LIST_member(access, T) \
219  if (defaultValue.is_none())\
220  {\
221  mReflector.access(name.c_str(),\
222  Getter<std::vector<T>>(boost::bind(&ReflectorWrapper<Reflector>::getList<T>, getterCallable)),\
223  Setter<std::vector<T>>(boost::bind(&ReflectorWrapper<Reflector>::setList<T>, setterCallable, _1)),\
224  comment.c_str());\
225  }\
226  else\
227  {\
228  std::vector<T> dv = listToVector<T>(defaultValue); \
229  mReflector.access(name.c_str(),\
230  Getter<std::vector<T>>(boost::bind(&ReflectorWrapper<Reflector>::getList<T>, getterCallable)),\
231  Setter<std::vector<T>>(boost::bind(&ReflectorWrapper<Reflector>::setList<T>, setterCallable, _1)),\
232  comment.c_str(),\
233  dv);\
234  }
235 
236 #define REFLECTOR_WRAPPER_CALL_LIST_roproperty(access, T) \
237  mReflector.access(name.c_str(),\
238  Getter<std::vector<T>>(boost::bind(&ReflectorWrapper<Reflector>::getList<T>, getterCallable)),\
239  comment.c_str());
240 
241 #define REFLECTOR_WRAPPER_CALL_property REFLECTOR_WRAPPER_CALL_member
242 #define REFLECTOR_WRAPPER_CALL_LIST_property REFLECTOR_WRAPPER_CALL_LIST_member
243 
244 #define REFLECTOR_WRAPPER_CHECK_GETSET_member \
245  if (!PyCallable_Check(getterCallable.ptr()))\
246  MIRA_THROW(XRuntime, "getter parameter is no callable object.");\
247  if (!PyCallable_Check(setterCallable.ptr())) \
248  MIRA_THROW(XRuntime, "setter parameter is no callable object.");\
249 
250 #define REFLECTOR_WRAPPER_CHECK_GETSET_roproperty \
251  if (!PyCallable_Check(getterCallable.ptr()))\
252  MIRA_THROW(XRuntime, "getter parameter is no callable object.");\
253 
254 #define REFLECTOR_WRAPPER_CHECK_GETSET_property REFLECTOR_WRAPPER_CHECK_GETSET_member
255 
256 #define REFLECTOR_WRAPPER_CALL_IF_TYPE(PyType, CType, access) \
257  if (PyType_IsSubtype((PyTypeObject*)type.ptr(), &PyType))\
258  {\
259  REFLECTOR_WRAPPER_CALL_##access(access, CType)\
260  return;\
261  }
262 
263 #define REFLECTOR_WRAPPER_CALL_IF_LIST_TYPE(PyType, CType, access) \
264  if (PyType_IsSubtype((PyTypeObject*)listType.ptr(), &PyType))\
265  {\
266  REFLECTOR_WRAPPER_CALL_LIST_##access(access, CType)\
267  return;\
268  }
269 
270 #ifdef MIRA_PYTHON3\
271  // https://docs.python.org/3/howto/cporting.html#long-int-unification
272  // https://docs.python.org/3/howto/cporting.html#str-unicode-unification
273  #define REFLECTOR_WRAPPER_FIND_TYPE(access) \
274  REFLECTOR_WRAPPER_CALL_IF_TYPE(PyBool_Type, bool, access) \
275  REFLECTOR_WRAPPER_CALL_IF_TYPE(PyLong_Type, int64, access) \
276  REFLECTOR_WRAPPER_CALL_IF_TYPE(PyFloat_Type, double, access) \
277  REFLECTOR_WRAPPER_CALL_IF_TYPE(PyUnicode_Type, std::string, access)
278 
279  #define REFLECTOR_WRAPPER_FIND_LIST_TYPE(access) \
280  REFLECTOR_WRAPPER_CALL_IF_LIST_TYPE(PyBool_Type, bool, access) \
281  REFLECTOR_WRAPPER_CALL_IF_LIST_TYPE(PyLong_Type, int64, access) \
282  REFLECTOR_WRAPPER_CALL_IF_LIST_TYPE(PyFloat_Type, double, access) \
283  REFLECTOR_WRAPPER_CALL_IF_LIST_TYPE(PyUnicode_Type, std::string, access)
284 #else
285  #define REFLECTOR_WRAPPER_FIND_TYPE(access) \
286  REFLECTOR_WRAPPER_CALL_IF_TYPE(PyBool_Type, bool, access) \
287  REFLECTOR_WRAPPER_CALL_IF_TYPE(PyInt_Type, int, access) \
288  REFLECTOR_WRAPPER_CALL_IF_TYPE(PyLong_Type, int64, access) \
289  REFLECTOR_WRAPPER_CALL_IF_TYPE(PyFloat_Type, double, access) \
290  REFLECTOR_WRAPPER_CALL_IF_TYPE(PyString_Type, std::string, access) \
291  REFLECTOR_WRAPPER_CALL_IF_TYPE(PyUnicode_Type, std::string, access)
292 
293  #define REFLECTOR_WRAPPER_FIND_LIST_TYPE(access) \
294  REFLECTOR_WRAPPER_CALL_IF_LIST_TYPE(PyBool_Type, bool, access) \
295  REFLECTOR_WRAPPER_CALL_IF_LIST_TYPE(PyInt_Type, int, access) \
296  REFLECTOR_WRAPPER_CALL_IF_LIST_TYPE(PyLong_Type, int64, access) \
297  REFLECTOR_WRAPPER_CALL_IF_LIST_TYPE(PyFloat_Type, double, access) \
298  REFLECTOR_WRAPPER_CALL_IF_LIST_TYPE(PyString_Type, std::string, access) \
299  REFLECTOR_WRAPPER_CALL_IF_LIST_TYPE(PyUnicode_Type, std::string, access)
300 #endif
301 
302 #define REFLECTOR_WRAPPER_ACCESS(access) \
303  using namespace boost::python;\
304  REFLECTOR_WRAPPER_CHECK_GETSET_##access\
305 \
306  if (PyObject_IsInstance(type.ptr(), (PyObject*) &PyList_Type))\
307  {\
308  int n = PyList_Size(type.ptr());\
309  if (n!=1)\
310  MIRA_THROW(XRuntime, "List must contain exactly one type information.");\
311  object listType = type[0];\
312  if (!PyType_Check(listType.ptr()))\
313  MIRA_THROW(XRuntime, "Type parameter in list is no type object.");\
314 \
315  REFLECTOR_WRAPPER_FIND_LIST_TYPE(access) \
316 \
317  std::string typeStr = extract<std::string>(str(listType));\
318  MIRA_THROW(XRuntime, "Reflect not implemented for primitive type " + typeStr + ".");\
319  }\
320  else\
321  {\
322  if (!PyType_Check(type.ptr()))\
323  MIRA_THROW(XRuntime, "Type parameter is no type object.");\
324 \
325  REFLECTOR_WRAPPER_FIND_TYPE(access) \
326 \
327  std::string typeStr = extract<std::string>(str(type));\
328  MIRA_THROW(XRuntime, "Reflect not implemented for primitive type " + typeStr + ".");\
329  }
330 
331  void memberWithCommentAndDefault(const std::string& name,
332  boost::python::object type,
333  boost::python::object getterCallable,
334  boost::python::object setterCallable,
335  const std::string& comment, boost::python::object defaultValue)
336  {
338  }
339 
340  void propertyWithCommentAndDefault(const std::string& name,
341  boost::python::object type,
342  boost::python::object getterCallable,
343  boost::python::object setterCallable,
344  const std::string& comment, boost::python::object defaultValue)
345  {
347  }
348 
349  void ropropertyWithComment(const std::string& name,
350  boost::python::object type,
351  boost::python::object getterCallable,
352  const std::string& comment)
353  {
355  }
356 
357 #undef REFLECTOR_WRAPPER_CALL
358 #undef REFLECTOR_WRAPPER_CALL_LIST
359 #undef REFLECTOR_WRAPPER_CALL_IF_TYPE
360 #undef REFLECTOR_WRAPPER_CALL_IF_LIST_TYPE
361 #undef REFLECTOR_WRAPPER_FIND_TYPE
362 #undef REFLECTOR_WRAPPER_FIND_LIST_TYPE
363 #undef REFLECTOR_WRAPPER_ACCESS
364 
365  void memberWithComment(const std::string& name,
366  boost::python::object type,
367  boost::python::object getterCallable,
368  boost::python::object setterCallable,
369  const std::string& comment)
370  {
371  using namespace boost::python;
372  memberWithCommentAndDefault(name, type, getterCallable, setterCallable, comment, object());
373  }
374 
375  void propertyWithComment(const std::string& name,
376  boost::python::object type,
377  boost::python::object getterCallable,
378  boost::python::object setterCallable,
379  const std::string& comment)
380  {
381  using namespace boost::python;
382  propertyWithCommentAndDefault(name, type, getterCallable, setterCallable, comment, object());
383  }
384 
385  void member(const std::string& name,
386  boost::python::object type,
387  boost::python::object getterCallable,
388  boost::python::object setterCallable)
389  {
390  using namespace boost::python;
391  memberWithComment(name, type, getterCallable, setterCallable, "");
392  }
393 
394  void property(const std::string& name,
395  boost::python::object type,
396  boost::python::object getterCallable,
397  boost::python::object setterCallable)
398  {
399  using namespace boost::python;
400  propertyWithComment(name, type, getterCallable, setterCallable, "");
401  }
402 
403  void roproperty(const std::string& name,
404  boost::python::object type,
405  boost::python::object getterCallable)
406  {
407  using namespace boost::python;
408  ropropertyWithComment(name, type, getterCallable, "");
409  }
410 
411  void interface(const std::string& name)
412  {
413  mReflector.interface(name.c_str());
414  }
415 
416  int argumentCount(boost::python::object callable)
417  {
418  using namespace boost::python;
419 
420  if (!PyCallable_Check(callable.ptr()))
421  MIRA_THROW(XRuntime, "callable parameter is no callable object.");
422 
423  int possibleSelfArg = 0;
424 
425 #ifdef MIRA_PYTHON3
426  const char * codeAttr = "__code__";
427  const char * selfAttr = "__self__";
428 #else
429  const char * codeAttr = "func_code";
430  const char * selfAttr = "im_self";
431 #endif
432  // Handle function objects vs. methods
433  // * python functions: have a 'func_code'/__code__ attribute
434  // * python methods: only their __call__ method has func_code/__code__
435  bool isMethod = PyObject_HasAttrString(callable.ptr(), codeAttr) == 0;
436  // TODO: Python's inspect module can likely help here
437 
438  object funcCode;
439  if (isMethod)
440  {
441  // method object, need to increment count for 'self'
442  possibleSelfArg = 1;
443  funcCode = callable.attr("__call__").attr(codeAttr);
444  }
445  else
446  {
447  funcCode = callable.attr(codeAttr);
448 
449  // Handle instancemethod type callable
450  if (PyObject_HasAttrString(callable.ptr(), selfAttr) == 1)
451  possibleSelfArg = 1;
452  }
453 
454  // 0x4 in the flags means we have a function signature with *args
455  int flags = extract<int>(funcCode.attr("co_flags"));
456  if (flags & 0x04)
457  MIRA_THROW(XRuntime, "RPC methods with dynamic number of arguments are not supported");
458 
459  int argCount = extract<int>(funcCode.attr("co_argcount")) - possibleSelfArg;
460 
461  if (argCount > RPC_METHODS_MAX_PARAMS-1)
462  MIRA_THROW(XRuntime, "RPC methods with more than " << RPC_METHODS_MAX_PARAMS-1 << " arguments are not supported");
463 
464  return argCount;
465  }
466 
467  template<typename ReturnType = json::Value>
468  void methodWithComment(const std::string& name, boost::python::object callable, const std::string& comment)
469  {
470  int argCount = argumentCount(callable);
471 
472  if (argCount == 0)
473  mReflector.template method<ReturnType>(name.c_str(), boost::bind(&ReflectorWrapperRPCCallHelper<ReturnType>::rpcCall0, callable), comment.c_str());
474 #define REFLECTORWRAPPERGEN_SERVICE_CALLS(z,n,_) \
475  if (argCount == n) \
476  mReflector.template method<ReturnType, BOOST_PP_ENUM_PARAMS(n, json::Value BOOST_PP_INTERCEPT)>(name.c_str(), boost::bind(&ReflectorWrapperRPCCallHelper<ReturnType>::BOOST_PP_CAT(rpcCall, n), callable, BOOST_PP_ENUM_SHIFTED_PARAMS_Z(z,BOOST_PP_INC(n), _)), comment.c_str());
478 #undef REFLECTORWRAPPERGEN_SERVICE_CALLS
479  }
480 
481 #define REFLECTORWRAPPERGEN_NAME_DESC_DECL(z,n,_) const std::string& name##n, const std::string& description##n
482 #define REFLECTORWRAPPERGEN_NAME_DESC(z,n,_) name##n.c_str(), description##n.c_str()
483 
484 #define REFLECTORWRAPPERGEN_SERVICE_CALLS_DESCRIPTIONS(z,n,_) \
485  template<typename ReturnType = json::Value> \
486  void methodWithParamDescription##n(const std::string& name, boost::python::object callable, const std::string& comment, \
487  BOOST_PP_ENUM(n, REFLECTORWRAPPERGEN_NAME_DESC_DECL, _)) \
488  { \
489  mReflector.template method<ReturnType, BOOST_PP_ENUM_PARAMS(n, json::Value BOOST_PP_INTERCEPT)>( \
490  name.c_str(), \
491  boost::bind(&ReflectorWrapperRPCCallHelper<ReturnType>::rpcCall##n, \
492  callable, BOOST_PP_ENUM_SHIFTED_PARAMS_Z(z, BOOST_PP_INC(n), _)), \
493  comment.c_str(), \
494  BOOST_PP_ENUM(n, REFLECTORWRAPPERGEN_NAME_DESC, _)); \
495  }
497 #undef REFLECTORWRAPPERGEN_SERVICE_CALLS_DESCRIPTIONS
498 #undef REFLECTORWRAPPERGEN_NAME_DESC
499 
500 // some extra empty methods - never called, but required to compile dispatch methods
501 #define REFLECTORWRAPPERGEN_SERVICE_CALLS_DESCRIPTIONS(z,n,_) \
502  template<typename ReturnType = json::Value> \
503  void methodWithParamDescription##n(const std::string& name, boost::python::object callable, const std::string& comment, \
504  BOOST_PP_ENUM(n, REFLECTORWRAPPERGEN_NAME_DESC_DECL, _)) {}
506 #undef REFLECTORWRAPPERGEN_SERVICE_CALLS_DESCRIPTIONS
507 #undef REFLECTORWRAPPERGEN_NAME_DESC_DECL
508 
509 // extra empty method - never called, but required to compile dispatch methods
510 template<typename ReturnType = json::Value> \
511 void methodWithParamDescriptionAndSample0(const std::string& name, boost::python::object callable, const std::string& comment) {}
512 
513 #define REFLECTORWRAPPERGEN_NAME_DESC_SAMPLE_DECL(z,n,_) const std::string& name##n, const std::string& description##n, boost::python::object sample##n
514 #define REFLECTORWRAPPERGEN_NAME_DESC_SAMPLE(z,n,_) name##n.c_str(), description##n.c_str(), \
515  boost::python::extract<json::Value>(PyObject_HasAttrString(sample##n.ptr(), "convertObjectToJSON") ? sample##n.attr("convertObjectToJSON")() : sample##n)()
516 
517 #define REFLECTORWRAPPERGEN_SERVICE_CALLS_DESCRIPTIONS_SAMPLES(z,n,_) \
518  template<typename ReturnType = json::Value> \
519  void methodWithParamDescriptionAndSample##n(const std::string& name, boost::python::object callable, const std::string& comment, \
520  BOOST_PP_ENUM(n, REFLECTORWRAPPERGEN_NAME_DESC_SAMPLE_DECL, _)) \
521  { \
522  boost::function<ReturnType(BOOST_PP_ENUM_PARAMS(n, json::Value BOOST_PP_INTERCEPT))> fn; \
523  fn = boost::bind(&ReflectorWrapperRPCCallHelper<json::Value>::rpcCall##n, \
524  callable, BOOST_PP_ENUM_SHIFTED_PARAMS_Z(z, BOOST_PP_INC(n), _)); \
525  \
526  mReflector.template method<ReturnType, BOOST_PP_ENUM_PARAMS(n, json::Value BOOST_PP_INTERCEPT)>( \
527  name.c_str(), fn, comment.c_str(), \
528  BOOST_PP_ENUM(n, REFLECTORWRAPPERGEN_NAME_DESC_SAMPLE, _)); \
529  }
531 #undef REFLECTORWRAPPERGEN_SERVICE_CALLS_DESCRIPTIONS_SAMPLES
532 #undef REFLECTORWRAPPERGEN_NAME_DESC_SAMPLE_DECL
533 #undef REFLECTORWRAPPERGEN_NAME_DESC_SAMPLE
534 
535 #define REFLECTORWRAPPERGEN_P_DECL(z,n,_) boost::python::object p##n
536 #define REFLECTORWRAPPERGEN_P_TO_STRING_P_TO_STRING(z,n,_) boost::python::extract<std::string>(BOOST_PP_CAT(p, BOOST_PP_MUL(2, n)))(), \
537  boost::python::extract<std::string>(BOOST_PP_CAT(p, BOOST_PP_INC(BOOST_PP_MUL(2, n))))()
538 #define REFLECTORWRAPPERGEN_P_TO_STRING_P_TO_STRING_OBJECT(z,n,_) boost::python::extract<std::string>(BOOST_PP_CAT(p, BOOST_PP_MUL(3, n)))(), \
539  boost::python::extract<std::string>(BOOST_PP_CAT(p, BOOST_PP_INC(BOOST_PP_MUL(3, n))))(), \
540  BOOST_PP_CAT(p, BOOST_PP_INC(BOOST_PP_INC(BOOST_PP_MUL(3, n))))
541 
542 // If we expose the above methods to Python directly, the Python interpreter cannot distinguish in all cases which one to use -
543 // e.g. with 6 extra params, these could be either 3x name+desc or 2x name+desc+sample. The correct one of course must match the number of
544 // arguments expected by callable, but this number is not known to the Python interpreter when it selects the overload, so given the choice
545 // between methodWithParamDescription3 and methodWithParamDescriptionAndSample2, it is not guaranteed to call the right one.
546 // The problem is solved by only exposing dispatch methods and deciding inside whether the params should form a sequence of name+description
547 // or of name+description+sample, after checking the number of arguments that callable takes.
548 #define REFLECTORWRAPPERGEN_SERVICE_CALLS_DISPATCH(z,n,_) \
549  template<typename ReturnType = json::Value> \
550  void methodDispatch##n(const std::string& name, boost::python::object callable, const std::string& comment, \
551  BOOST_PP_ENUM(n, REFLECTORWRAPPERGEN_P_DECL, _)) \
552  { \
553  int argCount = argumentCount(callable); \
554  if (argCount*2 == n) { \
555  this->template BOOST_PP_CAT(methodWithParamDescription, BOOST_PP_DIV(n,2))<ReturnType> \
556  (name, callable, comment \
557  BOOST_PP_ENUM_TRAILING(BOOST_PP_DIV(n,2), REFLECTORWRAPPERGEN_P_TO_STRING_P_TO_STRING, p)); \
558  return; \
559  } \
560  if (argCount*3 == n) { \
561  this->template BOOST_PP_CAT(methodWithParamDescriptionAndSample, BOOST_PP_DIV(n,3))<ReturnType> \
562  (name, callable, comment \
563  BOOST_PP_ENUM_TRAILING(BOOST_PP_DIV(n,3), REFLECTORWRAPPERGEN_P_TO_STRING_P_TO_STRING_OBJECT, p)); \
564  return; \
565  } \
566  \
567  MIRA_THROW(XRuntime, "Number of names/descriptions/sample values for parameters in call to Reflector::method('" << name \
568  << "', ...) do not match the provided function's signature (" << n << " equals neither 2*" \
569  << argCount << " nor 3*" << argCount << "). Please provide (string) name AND description for EACH "\
570  "RPC method parameter, or name, description AND sample value for EACH parameter."); \
571  }
573 #undef REFLECTORWRAPPERGEN_SERVICE_CALLS_DISPATCH
574 #undef REFLECTORWRAPPERGEN_P_DECL
575 #undef REFLECTORWRAPPERGEN_P_TO_STRING_P_TO_STRING
576 #undef REFLECTORWRAPPERGEN_P_TO_STRING_P_TO_STRING_OBJECT
577 
578  template<typename ReturnType = json::Value>
579  void method(const std::string& name, boost::python::object callable)
580  {
581  methodWithComment<ReturnType>(name, callable, "");
582  }
583 
584 protected:
585 
586  template<typename T>
587  static T get(boost::python::object callable)
588  {
589  using namespace boost::python;
590 
591  ScopedGILLock lock;
592  try
593  {
594  boost::python::object value = callable();
595  return boost::python::extract<T>(value);
596  }
597  catch(boost::python::error_already_set&)
598  {
599  MIRA_THROW(XInvalidParameter, "Error calling getter: " << getLastExceptionString());
600  }
601  return T();
602  }
603 
604  template<typename T>
605  static void set(boost::python::object callable, const T& value)
606  {
607  ScopedGILLock lock;
608  try
609  {
610  callable(boost::python::object(value));
611  }
612  catch(boost::python::error_already_set&)
613  {
614  MIRA_THROW(XInvalidParameter, "Error calling setter: " << getLastExceptionString());
615  }
616  }
617 
618  template<typename T>
619  static std::vector<T> getList(boost::python::object callable)
620  {
621  using namespace boost::python;
622 
623  ScopedGILLock lock;
624  try
625  {
626  boost::python::object value = callable();
627  return listToVector<T>(value);
628  }
629  catch(boost::python::error_already_set&)
630  {
631  MIRA_THROW(XInvalidParameter, "Error calling getter: " << getLastExceptionString());
632  }
633  return std::vector<T>();
634  }
635 
636  template<typename T>
637  static void setList(boost::python::object callable, const std::vector<T>& value)
638  {
639  ScopedGILLock lock;
640  boost::python::list l;
641  foreach(const T& t, value)
642  l.append(t);
643  try
644  {
645  callable(boost::python::object(l));
646  }
647  catch(boost::python::error_already_set&)
648  {
649  MIRA_THROW(XInvalidParameter, "Error calling setter: " << getLastExceptionString());
650  }
651  }
652 
653  Reflector& mReflector;
654 };
655 
657 
658 }}
659 
660 #endif /* _MIRA_TOOLBOXES_PYTHON_REFLECTORWRAPPER_H_ */
661 
static serialization::VersionType versionHelp(MetaSerializer &r, serialization::VersionType version, boost::python::object callerType)
Definition: ReflectorWrapper.h:79
void ropropertyWithComment(const std::string &name, boost::python::object type, boost::python::object getterCallable, const std::string &comment)
Definition: ReflectorWrapper.h:349
void propertyWithComment(const std::string &name, boost::python::object type, boost::python::object getterCallable, boost::python::object setterCallable, const std::string &comment)
Definition: ReflectorWrapper.h:375
virtual void property(const std::string &name, boost::python::object getterCallable, boost::python::object setterCallable)
Definition: ReflectorWrapper.h:148
void methodWithComment(const std::string &name, boost::python::object callable, const std::string &comment)
Definition: ReflectorWrapper.h:468
int argumentCount(boost::python::object callable)
Definition: ReflectorWrapper.h:416
std::vector< T > listToVector(const boost::python::object &l)
Definition: ReflectorWrapper.h:57
void member(const std::string &name, boost::python::object type, boost::python::object getterCallable, boost::python::object setterCallable)
Definition: ReflectorWrapper.h:385
virtual ~BaseReflectorWrapper()
Definition: ReflectorWrapper.h:137
ReflectorWrapperVersionHelper< MetaSerializer > Derived
Definition: ReflectorWrapper.h:78
void propertyWithCommentAndDefault(const std::string &name, boost::python::object type, boost::python::object getterCallable, boost::python::object setterCallable, const std::string &comment, boost::python::object defaultValue)
Definition: ReflectorWrapper.h:340
#define MIRA_THROW(ex, msg)
virtual void interface(const std::string &name)
Definition: ReflectorWrapper.h:155
#define RPC_METHODS_MAX_PARAMS
Definition: RPCDefinitions.h:33
#define MIRA_REFLECT_CALL(ReflectorType, reflector, context, COMMAND)
Definition: ReflectorWrapper.h:92
Defines to handle different Python versions.
PropertyHint type(const std::string &t)
Exception handling for python exceptions.
Definition: ReflectorWrapper.h:133
void roproperty(const std::string &name, boost::python::object type, boost::python::object getterCallable)
Definition: ReflectorWrapper.h:403
void reflectCall(const std::string &context, boost::python::object callable)
Definition: ReflectorWrapper.h:181
Definition: ReflectorWrapper.h:64
static serialization::VersionType versionHelp(Reflector &r, serialization::VersionType version, boost::python::object callerType)
Definition: ReflectorWrapper.h:66
A scoped global interpreter (GIL) lock.
Definition: ScopedGIL.h:54
void property(const std::string &name, boost::python::object type, boost::python::object getterCallable, boost::python::object setterCallable)
Definition: ReflectorWrapper.h:394
void memberWithCommentAndDefault(const std::string &name, boost::python::object type, boost::python::object getterCallable, boost::python::object setterCallable, const std::string &comment, boost::python::object defaultValue)
Definition: ReflectorWrapper.h:331
#define REFLECTORWRAPPERGEN_SERVICE_CALLS_DESCRIPTIONS(z, n, _)
Definition: ReflectorWrapper.h:501
#define REFLECTOR_WRAPPER_ACCESS(access)
Definition: ReflectorWrapper.h:302
#define REFLECTORWRAPPERGEN_SERVICE_CALLS_DISPATCH(z, n, _)
Include this instead of boost/python.hpp to reduce compile time warning spam from Boost internal inco...
#define REFLECTORWRAPPERGEN_SERVICE_CALLS_DESCRIPTIONS_SAMPLES(z, n, _)
#define REFLECTORWRAPPERGEN_SERVICE_CALLS(z, n, _)
virtual void member(const std::string &name, boost::python::object getterCallable, boost::python::object setterCallable)
Definition: ReflectorWrapper.h:141
std::string getLastExceptionString()
Extract the message from the last python exception.
BOOST_PP_REPEAT_FROM_TO(RPC_METHODS_MAX_PARAMS, BOOST_PP_DIV(BOOST_PP_MUL(3, RPC_METHODS_MAX_PARAMS), 2), REFLECTORWRAPPERGEN_SERVICE_CALLS_DESCRIPTIONS, _) template< typename ReturnType
serialization::VersionType version(serialization::VersionType version, boost::python::object callerType)
Definition: ReflectorWrapper.h:190
Definition: ReflectorWrapper.h:168
void interface(const std::string &name)
Definition: ReflectorWrapper.h:411
void memberWithComment(const std::string &name, boost::python::object type, boost::python::object getterCallable, boost::python::object setterCallable, const std::string &comment)
Definition: ReflectorWrapper.h:365
#define REFLECTORWRAPPERGEN_SERVICE_METHODS(z, n, _)
Definition: ReflectorWrapper.h:116
ReflectorWrapper(Reflector &r)
Definition: ReflectorWrapper.h:172
virtual ~ReflectorWrapper()
Definition: ReflectorWrapper.h:177
virtual void method(const std::string &name, boost::python::object callable)
Definition: ReflectorWrapper.h:160