HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pyPolymorphic.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_BASE_TF_PY_POLYMORPHIC_H
8 #define PXR_BASE_TF_PY_POLYMORPHIC_H
9 
10 /// \file tf/pyPolymorphic.h
11 
12 #include "pxr/pxr.h"
13 
14 #include "pxr/base/tf/pyOverride.h"
15 
16 #include "pxr/base/tf/refPtr.h"
17 #include "pxr/base/tf/weakPtr.h"
18 #include "pxr/base/tf/diagnostic.h"
19 #include "pxr/base/tf/pyCall.h"
20 #include "pxr/base/tf/pyLock.h"
21 #include "pxr/base/tf/type.h"
22 
23 #include "pxr/external/boost/python/object/class_detail.hpp"
24 #include "pxr/external/boost/python/wrapper.hpp"
25 #include "pxr/external/boost/python/has_back_reference.hpp"
26 
27 #include <functional>
28 #include <type_traits>
29 
30 // TODO: All this stuff with holding onto the class needs to go away.
31 
33 
34 template <typename Derived>
36  public TfType::PyPolymorphicBase,
37  public pxr_boost::python::wrapper<Derived>
38 {
41 
42  Override GetOverride(char const *func) const {
43  TfPyLock pyLock;
44 
45  using namespace pxr_boost::python;
46 
47  // don't use pxr_boost::python::wrapper::get_override(), as it can return
48  // the wrong result. instead, implement our own version which does
49  // better
50 
51  PyObject * m_self = detail::wrapper_base_::get_owner(*this);
52  if (m_self) {
53 
54  // using pythons mro, get the attribute string that represents
55  // the named function. this will return something valid if it exists
56  // in this or any ancestor class
57  if (handle<> m = handle<>(
58  allow_null(
59  PyObject_GetAttrString(
60  m_self, const_cast<char*>(func))))
61  )
62  {
63  // now get the typehandle to the class. we will use this to
64  // determine if this method exists on the derived class
65  type_handle typeHandle =
66  objects::registered_class_object(
67  typeid(Derived));
68  PyTypeObject* class_object = typeHandle.get();
69 
70  PyObject* func_object = 0;
71 
72  if (
73  PyMethod_Check(m.get())
74  && ((PyMethodObject*)m.get())->im_self == m_self
75  && class_object->tp_dict != 0
76  )
77  {
78  // look for the method on the class object.
79  handle<> borrowed_f(
80  allow_null(
81  PyObject_GetAttrString(
82  (PyObject *)class_object,
83  const_cast<char*>(func))));
84 
85  // Don't leave an exception if there's no base class method
86  PyErr_Clear();
87 
88  // do the appropriate conversion, if possible
89  if (borrowed_f && PyCallable_Check(borrowed_f.get())) {
90  func_object = borrowed_f.get();
91  }
92  }
93 
94  // now, func_object is either NULL, or pointing at the method
95  // on the class or one of it's ancestors. m is holding the
96  // actual method that pythons mro would find. if that thing
97  // is not the same, it must be an override
98  if (func_object != ((PyMethodObject*)m.get())->im_func)
99  return Override(m);
100  }
101  }
102  PyErr_Clear(); // Don't leave an exception if there's no override.
103 
104  return Override(handle<>(detail::none()));
105  }
106 
107  Override GetPureOverride(char const *func) const {
108  TfPyLock pyLock;
109  Override ret = GetOverride(func);
110  if (!ret) {
111  // Raise a *python* exception when no virtual is found. This is
112  // because a subsequent attempt to call ret will result in a python
113  // exception, but a far less useful one. If we were to simply make
114  // a TfError here, it would be trumped by that python exception.
115  PyErr_SetString(PyExc_AttributeError, TfStringPrintf
116  ("Pure virtual method '%s' called -- "
117  "must provide a python implementation.",
118  func).c_str());
120  }
121  return ret;
122  }
123 
124  template <typename Ret>
125  TfPyCall<Ret> CallPureVirtual(char const *func) const {
126  TfPyLock lock;
127  return TfPyCall<Ret>(GetPureOverride(func));
128  }
129 
130  template <class Ret, class Cls, typename... Arg>
131  std::function<Ret (Arg...)>
132  CallVirtual(
133  char const *fname,
134  Ret (Cls::*defaultImpl)(Arg...));
135 
136  template <class Ret, class Cls, typename... Arg>
137  std::function<Ret (Arg...)>
138  CallVirtual(
139  char const *fname,
140  Ret (Cls::*defaultImpl)(Arg...) const) const;
141 
142 protected:
143  virtual ~TfPyPolymorphic();
144 
145 private:
146 
147  // Helper to bind a pointer-to-member-function and a pointer to an
148  // instance.
149  template <class Ret, class Cls, typename... Args>
150  struct _BindMemFn
151  {
152  using MemFn = typename std::conditional<
154  Ret (Cls::*)(Args...) const, Ret (Cls::*)(Args...)>::type;
155 
156  _BindMemFn(MemFn memFn, Cls *obj)
157  : _memFn(memFn)
158  , _obj(obj)
159  {}
160 
161  Ret
162  operator()(Args... args) const
163  {
164  return (_obj->*_memFn)(args...);
165  }
166 
167  private:
168  MemFn _memFn;
169  Cls *_obj;
170  };
171 };
172 
173 template <typename Derived>
175 {
176 }
177 
178 template <typename Derived>
179 template <class Ret, class Cls, typename... Args>
180 inline
181 std::function<Ret (Args...)>
183  char const *fname,
184  Ret (Cls::*defaultImpl)(Args...))
185 {
186  static_assert(std::is_base_of<This, Cls>::value,
187  "This must be a base of Cls.");
188  TfPyLock lock;
189  if (Override o = GetOverride(fname))
190  return std::function<Ret (Args...)>(TfPyCall<Ret>(o));
191  return _BindMemFn<Ret, Cls, Args...>(
192  defaultImpl, static_cast<Cls *>(this));
193 }
194 
195 template <typename Derived>
196 template <class Ret, class Cls, typename... Args>
197 inline
198 std::function<Ret (Args...)>
200  char const *fname,
201  Ret (Cls::*defaultImpl)(Args...) const) const
202 {
203  static_assert(std::is_base_of<This, Cls>::value,
204  "This must be a base of Cls.");
205  TfPyLock lock;
206  if (Override o = GetOverride(fname))
207  return std::function<Ret (Args...)>(TfPyCall<Ret>(o));
208  return _BindMemFn<Ret, Cls const, Args...>(
209  defaultImpl, static_cast<Cls const *>(this));
210 }
211 
213 
214 // Specialize has_back_reference<> so that boost.python will pass
215 // PyObject* as the 1st argument to TfPyPolymorphic's ctor.
216 namespace PXR_BOOST_NAMESPACE { namespace python {
217  template <typename T>
218  struct has_back_reference< PXR_NS::TfPyPolymorphic<T> >
219  : std::true_type {};
220 }}
221 
223 
224 // Base case for internal Tf_PyMemberFunctionPointerUpcast.
225 template <typename Base, typename Fn>
227 
228 template <typename Base, typename Derived,
229  typename Ret, typename... Args>
230 struct Tf_PyMemberFunctionPointerUpcast< Base, Ret (Derived::*)(Args...) >
231 {
232  typedef Ret (Base::*Type)(Args...);
233 };
234 
235 template <typename Base, typename Derived,
236  typename Ret, typename... Args>
237 struct Tf_PyMemberFunctionPointerUpcast< Base, Ret (Derived::*)(Args...) const >
238 {
239  typedef Ret (Base::*Type)(Args...) const;
240 };
241 
242 template <typename Base, typename Fn>
245 {
247 
248  return static_cast<Ret>(fn);
249 }
250 
252 
253 #endif // PXR_BASE_TF_PY_POLYMORPHIC_H
type
Definition: core.h:556
TF_API std::string TfStringPrintf(const char *fmt,...)
virtual ~TfPyPolymorphic()
GLsizei const GLfloat * value
Definition: glcorearb.h:824
TfPyCall< Ret > CallPureVirtual(char const *func) const
TfPyPolymorphic< Derived > This
Definition: pyPolymorphic.h:39
#define PXR_NS
Definition: pxr.h:54
TF_API void TfPyConvertPythonExceptionToTfErrors()
Override GetOverride(char const *func) const
Definition: pyPolymorphic.h:42
GLenum func
Definition: glcorearb.h:783
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
TfPyOverride Override
Definition: pyPolymorphic.h:40
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
**If you just want to fire and args
Definition: thread.h:618
OIIO_UTIL_API const char * c_str(string_view str)
std::function< Ret(Arg...)> CallVirtual(char const *fname, Ret(Cls::*defaultImpl)(Arg...))
Tf_PyMemberFunctionPointerUpcast< Base, Fn >::Type TfPyProtectedVirtual(Fn fn)
Override GetPureOverride(char const *func) const