Loading...
Searching...
No Matches
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
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"
36#include "pxr/base/tf/pyCall.h"
37#include "pxr/base/tf/pyLock.h"
38#include "pxr/base/tf/type.h"
39
40#include <boost/python/object/class_detail.hpp>
41#include <boost/python/wrapper.hpp>
42#include <boost/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
49PXR_NAMESPACE_OPEN_SCOPE
50
51template <typename Derived>
52struct TfPyPolymorphic :
53 public TfType::PyPolymorphicBase,
54 public boost::python::wrapper<Derived>
55{
56 typedef TfPyPolymorphic<Derived> This;
57 typedef TfPyOverride Override;
58
59 Override GetOverride(char const *func) const {
60 TfPyLock pyLock;
61
62 using namespace boost::python;
63
64 // don't use boost::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());
136 TfPyConvertPythonExceptionToTfErrors();
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
159protected:
160 virtual ~TfPyPolymorphic();
161
162private:
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<
170 std::is_const<Cls>::value,
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
190template <typename Derived>
191TfPyPolymorphic<Derived>::~TfPyPolymorphic()
192{
193}
194
195template <typename Derived>
196template <class Ret, class Cls, typename... Args>
197inline
198std::function<Ret (Args...)>
199TfPyPolymorphic<Derived>::CallVirtual(
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
212template <typename Derived>
213template <class Ret, class Cls, typename... Args>
214inline
215std::function<Ret (Args...)>
216TfPyPolymorphic<Derived>::CallVirtual(
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
229PXR_NAMESPACE_CLOSE_SCOPE
230
231// Specialize has_back_reference<> so that boost.python will pass
232// PyObject* as the 1st argument to TfPyPolymorphic's ctor.
233namespace boost { namespace python {
234 template <typename T>
235 struct has_back_reference< PXR_NS::TfPyPolymorphic<T> >
236 : mpl::true_ {};
237}} // end namespace boost
238
239PXR_NAMESPACE_OPEN_SCOPE
240
241// Base case for internal Tf_PyMemberFunctionPointerUpcast.
242template <typename Base, typename Fn>
243struct Tf_PyMemberFunctionPointerUpcast;
244
245template <typename Base, typename Derived,
246 typename Ret, typename... Args>
247struct Tf_PyMemberFunctionPointerUpcast< Base, Ret (Derived::*)(Args...) >
248{
249 typedef Ret (Base::*Type)(Args...);
250};
251
252template <typename Base, typename Derived,
253 typename Ret, typename... Args>
254struct Tf_PyMemberFunctionPointerUpcast< Base, Ret (Derived::*)(Args...) const >
255{
256 typedef Ret (Base::*Type)(Args...) const;
257};
258
259template <typename Base, typename Fn>
260typename Tf_PyMemberFunctionPointerUpcast<Base, Fn>::Type
261TfPyProtectedVirtual( Fn fn )
262{
263 typedef typename Tf_PyMemberFunctionPointerUpcast<Base, Fn>::Type Ret;
264
265 return static_cast<Ret>(fn);
266}
267
268PXR_NAMESPACE_CLOSE_SCOPE
269
270#endif // PXR_BASE_TF_PY_POLYMORPHIC_H
Low-level utilities for informing users of various internal and external diagnostic conditions.
Convenience class for accessing the Python Global Interpreter Lock.
Definition: pyLock.h:122
A reimplementation of boost::python::override.
Definition: pyOverride.h:115
TF_API std::string TfStringPrintf(const char *fmt,...)
Returns a string formed by a printf()-like specification.
Utilities for calling python callables.
Reference counting.
Provide a way to call a Python callable.
Definition: pyCall.h:57
Pointer storage with deletion detection.