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 Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_BASE_TF_PY_POLYMORPHIC_H
25 #define PXR_BASE_TF_PY_POLYMORPHIC_H
26 
27 /// \file tf/pyPolymorphic.h
28 
29 #include "pxr/pxr.h"
30 
31 #include "pxr/base/tf/pyOverride.h"
32 
33 #include "pxr/base/tf/refPtr.h"
34 #include "pxr/base/tf/weakPtr.h"
35 #include "pxr/base/tf/diagnostic.h"
36 #include "pxr/base/tf/pyCall.h"
37 #include "pxr/base/tf/pyLock.h"
38 #include "pxr/base/tf/type.h"
39 
40 #include <hboost/python/object/class_detail.hpp>
41 #include <hboost/python/wrapper.hpp>
42 #include <hboost/python/has_back_reference.hpp>
43 
44 #include <functional>
45 #include <type_traits>
46 
47 // TODO: All this stuff with holding onto the class needs to go away.
48 
50 
51 template <typename Derived>
53  public TfType::PyPolymorphicBase,
54  public hboost::python::wrapper<Derived>
55 {
58 
59  Override GetOverride(char const *func) const {
60  TfPyLock pyLock;
61 
62  using namespace hboost::python;
63 
64  // don't use hboost::python::wrapper::get_override(), as it can return
65  // the wrong result. instead, implement our own version which does
66  // better
67 
68  PyObject * m_self = detail::wrapper_base_::get_owner(*this);
69  if (m_self) {
70 
71  // using pythons mro, get the attribute string that represents
72  // the named function. this will return something valid if it exists
73  // in this or any ancestor class
74  if (handle<> m = handle<>(
75  allow_null(
76  PyObject_GetAttrString(
77  m_self, const_cast<char*>(func))))
78  )
79  {
80  // now get the typehandle to the class. we will use this to
81  // determine if this method exists on the derived class
82  type_handle typeHandle =
83  objects::registered_class_object(
84  typeid(Derived));
85  PyTypeObject* class_object = typeHandle.get();
86 
87  PyObject* func_object = 0;
88 
89  if (
90  PyMethod_Check(m.get())
91  && ((PyMethodObject*)m.get())->im_self == m_self
92  && class_object->tp_dict != 0
93  )
94  {
95  // look for the method on the class object.
96  handle<> borrowed_f(
97  allow_null(
98  PyObject_GetAttrString(
99  (PyObject *)class_object,
100  const_cast<char*>(func))));
101 
102  // Don't leave an exception if there's no base class method
103  PyErr_Clear();
104 
105  // do the appropriate conversion, if possible
106  if (borrowed_f && PyCallable_Check(borrowed_f.get())) {
107  func_object = borrowed_f.get();
108  }
109  }
110 
111  // now, func_object is either NULL, or pointing at the method
112  // on the class or one of it's ancestors. m is holding the
113  // actual method that pythons mro would find. if that thing
114  // is not the same, it must be an override
115  if (func_object != ((PyMethodObject*)m.get())->im_func)
116  return Override(m);
117  }
118  }
119  PyErr_Clear(); // Don't leave an exception if there's no override.
120 
121  return Override(handle<>(detail::none()));
122  }
123 
124  Override GetPureOverride(char const *func) const {
125  TfPyLock pyLock;
126  Override ret = GetOverride(func);
127  if (!ret) {
128  // Raise a *python* exception when no virtual is found. This is
129  // because a subsequent attempt to call ret will result in a python
130  // exception, but a far less useful one. If we were to simply make
131  // a TfError here, it would be trumped by that python exception.
132  PyErr_SetString(PyExc_AttributeError, TfStringPrintf
133  ("Pure virtual method '%s' called -- "
134  "must provide a python implementation.",
135  func).c_str());
137  }
138  return ret;
139  }
140 
141  template <typename Ret>
142  TfPyCall<Ret> CallPureVirtual(char const *func) const {
143  TfPyLock lock;
144  return TfPyCall<Ret>(GetPureOverride(func));
145  }
146 
147  template <class Ret, class Cls, typename... Arg>
148  std::function<Ret (Arg...)>
149  CallVirtual(
150  char const *fname,
151  Ret (Cls::*defaultImpl)(Arg...));
152 
153  template <class Ret, class Cls, typename... Arg>
154  std::function<Ret (Arg...)>
155  CallVirtual(
156  char const *fname,
157  Ret (Cls::*defaultImpl)(Arg...) const) const;
158 
159 protected:
160  virtual ~TfPyPolymorphic();
161 
162 private:
163 
164  // Helper to bind a pointer-to-member-function and a pointer to an
165  // instance.
166  template <class Ret, class Cls, typename... Args>
167  struct _BindMemFn
168  {
169  using MemFn = typename std::conditional<
171  Ret (Cls::*)(Args...) const, Ret (Cls::*)(Args...)>::type;
172 
173  _BindMemFn(MemFn memFn, Cls *obj)
174  : _memFn(memFn)
175  , _obj(obj)
176  {}
177 
178  Ret
179  operator()(Args... args) const
180  {
181  return (_obj->*_memFn)(args...);
182  }
183 
184  private:
185  MemFn _memFn;
186  Cls *_obj;
187  };
188 };
189 
190 template <typename Derived>
192 {
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...))
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, Args...>(
209  defaultImpl, static_cast<Cls *>(this));
210 }
211 
212 template <typename Derived>
213 template <class Ret, class Cls, typename... Args>
214 inline
215 std::function<Ret (Args...)>
217  char const *fname,
218  Ret (Cls::*defaultImpl)(Args...) const) const
219 {
220  static_assert(std::is_base_of<This, Cls>::value,
221  "This must be a base of Cls.");
222  TfPyLock lock;
223  if (Override o = GetOverride(fname))
224  return std::function<Ret (Args...)>(TfPyCall<Ret>(o));
225  return _BindMemFn<Ret, Cls const, Args...>(
226  defaultImpl, static_cast<Cls const *>(this));
227 }
228 
230 
231 // Specialize has_back_reference<> so that hboost.python will pass
232 // PyObject* as the 1st argument to TfPyPolymorphic's ctor.
233 namespace hboost { namespace python {
234  template <typename T>
235  struct has_back_reference< PXR_NS::TfPyPolymorphic<T> >
236  : mpl::true_ {};
237 }} // end namespace hboost
238 
240 
241 // Base case for internal Tf_PyMemberFunctionPointerUpcast.
242 template <typename Base, typename Fn>
244 
245 template <typename Base, typename Derived,
246  typename Ret, typename... Args>
247 struct Tf_PyMemberFunctionPointerUpcast< Base, Ret (Derived::*)(Args...) >
248 {
249  typedef Ret (Base::*Type)(Args...);
250 };
251 
252 template <typename Base, typename Derived,
253  typename Ret, typename... Args>
254 struct Tf_PyMemberFunctionPointerUpcast< Base, Ret (Derived::*)(Args...) const >
255 {
256  typedef Ret (Base::*Type)(Args...) const;
257 };
258 
259 template <typename Base, typename Fn>
262 {
264 
265  return static_cast<Ret>(fn);
266 }
267 
269 
270 #endif // PXR_BASE_TF_PY_POLYMORPHIC_H
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:56
#define PXR_NS
Definition: pxr.h:71
TF_API void TfPyConvertPythonExceptionToTfErrors()
Override GetOverride(char const *func) const
Definition: pyPolymorphic.h:59
GLenum func
Definition: glcorearb.h:783
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1432
TfPyOverride Override
Definition: pyPolymorphic.h:57
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91
**If you just want to fire and args
Definition: thread.h:609
std::function< Ret(Arg...)> CallVirtual(char const *fname, Ret(Cls::*defaultImpl)(Arg...))
Tf_PyMemberFunctionPointerUpcast< Base, Fn >::Type TfPyProtectedVirtual(Fn fn)
type
Definition: core.h:1059
Override GetPureOverride(char const *func) const