Logo ROOT  
Reference Guide
CPPDataMember.cxx
Go to the documentation of this file.
1 // Bindings
2 #include "CPyCppyy.h"
3 #include "PyStrings.h"
4 #include "CPPDataMember.h"
5 #include "CPPInstance.h"
6 #include "LowLevelViews.h"
7 #include "PyStrings.h"
8 #include "Utility.h"
9 
10 // Standard
11 #include <algorithm>
12 #include <vector>
13 #include <limits.h>
14 
15 
16 namespace CPyCppyy {
17 
19  kNone = 0x0000,
20  kIsStaticData = 0x0001,
21  kIsConstData = 0x0002,
22  kIsArrayType = 0x0004,
23  kIsCachable = 0x0008
24 };
25 
26 //= CPyCppyy data member as Python property behavior =========================
27 static PyObject* pp_get(CPPDataMember* pyprop, CPPInstance* pyobj, PyObject* /* kls */)
28 {
29 // cache lookup for low level views
30  if (pyprop->fFlags & kIsCachable) {
32  for (auto it = cache.begin(); it != cache.end(); ++it) {
33  if (it->first == pyprop->fOffset) {
34  if (it->second) {
35  Py_INCREF(it->second);
36  return it->second;
37  } else
38  cache.erase(it);
39  break;
40  }
41  }
42  }
43 
44 // normal getter access
45  void* address = pyprop->GetAddress(pyobj);
46  if (!address || (intptr_t)address == -1 /* Cling error */)
47  return nullptr;
48 
49 // for fixed size arrays
50  void* ptr = address;
51  if (pyprop->fFlags & kIsArrayType)
52  ptr = &address;
53 
54 // non-initialized or public data accesses through class (e.g. by help())
55  if (!ptr || (intptr_t)ptr == -1 /* Cling error */) {
56  Py_INCREF(pyprop);
57  return (PyObject*)pyprop;
58  }
59 
60  if (pyprop->fConverter != 0) {
61  PyObject* result = pyprop->fConverter->FromMemory(ptr);
62  if (!result)
63  return result;
64 
65  // low level views are expensive to create, so cache them on the object instead
66  bool isLLView = LowLevelView_CheckExact(result);
67  if (isLLView && CPPInstance_Check(pyobj)) {
68  Py_INCREF(result);
69  pyobj->GetDatamemberCache().push_back(std::make_pair(pyprop->fOffset, result));
70  pyprop->fFlags |= kIsCachable;
71  }
72 
73  // ensure that the encapsulating class does not go away for the duration
74  // of the data member's lifetime, if it is a bound type (it doesn't matter
75  // for builtin types, b/c those are copied over into python types and thus
76  // end up being "stand-alone")
77  // TODO: should be done for LLViews as well
78  else if (pyobj && CPPInstance_Check(result)) {
79  if (PyObject_SetAttr(result, PyStrings::gLifeLine, (PyObject*)pyobj) == -1)
80  PyErr_Clear(); // ignored
81  }
82 
83  return result;
84  }
85 
86  PyErr_Format(PyExc_NotImplementedError,
87  "no converter available for \"%s\"", pyprop->GetName().c_str());
88  return nullptr;
89 }
90 
91 //-----------------------------------------------------------------------------
92 static int pp_set(CPPDataMember* pyprop, CPPInstance* pyobj, PyObject* value)
93 {
94 // Set the value of the C++ datum held.
95  const int errret = -1;
96 
97 // filter const objects to prevent changing their values
98  if (pyprop->fFlags & kIsConstData) {
99  PyErr_SetString(PyExc_TypeError, "assignment to const data not allowed");
100  return errret;
101  }
102 
103 // remove cached low level view, if any (will be restored upon reaeding)
104  if (pyprop->fFlags & kIsCachable) {
106  for (auto it = cache.begin(); it != cache.end(); ++it) {
107  if (it->first == pyprop->fOffset) {
108  Py_XDECREF(it->second);
109  cache.erase(it);
110  break;
111  }
112  }
113  }
114 
115  intptr_t address = (intptr_t)pyprop->GetAddress(pyobj);
116  if (!address || address == -1 /* Cling error */)
117  return errret;
118 
119 // for fixed size arrays
120  void* ptr = (void*)address;
121  if (pyprop->fFlags & kIsArrayType)
122  ptr = &address;
123 
124 // actual conversion; return on success
125  if (pyprop->fConverter && pyprop->fConverter->ToMemory(value, ptr))
126  return 0;
127 
128 // set a python error, if not already done
129  if (!PyErr_Occurred())
130  PyErr_SetString(PyExc_RuntimeError, "property type mismatch or assignment not allowed");
131 
132 // failure ...
133  return errret;
134 }
135 
136 //= CPyCppyy data member construction/destruction ===========================
137 static CPPDataMember* pp_new(PyTypeObject* pytype, PyObject*, PyObject*)
138 {
139 // Create and initialize a new property descriptor.
140  CPPDataMember* pyprop = (CPPDataMember*)pytype->tp_alloc(pytype, 0);
141 
142  pyprop->fOffset = 0;
143  pyprop->fFlags = 0;
144  pyprop->fConverter = nullptr;
145  pyprop->fEnclosingScope = 0;
146  pyprop->fName = nullptr;
147 
148  return pyprop;
149 }
150 
151 //----------------------------------------------------------------------------
152 static void pp_dealloc(CPPDataMember* pyprop)
153 {
154 // Deallocate memory held by this descriptor.
155  using namespace std;
156  if (pyprop->fConverter && pyprop->fConverter->HasState()) delete pyprop->fConverter;
157  Py_XDECREF(pyprop->fName); // never exposed so no GC necessary
158 
159  Py_TYPE(pyprop)->tp_free((PyObject*)pyprop);
160 }
161 
162 
163 //= CPyCppyy data member type ================================================
164 PyTypeObject CPPDataMember_Type = {
165  PyVarObject_HEAD_INIT(&PyType_Type, 0)
166  (char*)"cppyy.CPPDataMember", // tp_name
167  sizeof(CPPDataMember), // tp_basicsize
168  0, // tp_itemsize
169  (destructor)pp_dealloc, // tp_dealloc
170  0, // tp_print
171  0, // tp_getattr
172  0, // tp_setattr
173  0, // tp_compare
174  0, // tp_repr
175  0, // tp_as_number
176  0, // tp_as_sequence
177  0, // tp_as_mapping
178  0, // tp_hash
179  0, // tp_call
180  0, // tp_str
181  0, // tp_getattro
182  0, // tp_setattro
183  0, // tp_as_buffer
184  Py_TPFLAGS_DEFAULT, // tp_flags
185  (char*)"cppyy data member (internal)", // tp_doc
186  0, // tp_traverse
187  0, // tp_clear
188  0, // tp_richcompare
189  0, // tp_weaklistoffset
190  0, // tp_iter
191  0, // tp_iternext
192  0, // tp_methods
193  0, // tp_members
194  0, // tp_getset
195  0, // tp_base
196  0, // tp_dict
197  (descrgetfunc)pp_get, // tp_descr_get
198  (descrsetfunc)pp_set, // tp_descr_set
199  0, // tp_dictoffset
200  0, // tp_init
201  0, // tp_alloc
202  (newfunc)pp_new, // tp_new
203  0, // tp_free
204  0, // tp_is_gc
205  0, // tp_bases
206  0, // tp_mro
207  0, // tp_cache
208  0, // tp_subclasses
209  0 // tp_weaklist
210 #if PY_VERSION_HEX >= 0x02030000
211  , 0 // tp_del
212 #endif
213 #if PY_VERSION_HEX >= 0x02060000
214  , 0 // tp_version_tag
215 #endif
216 #if PY_VERSION_HEX >= 0x03040000
217  , 0 // tp_finalize
218 #endif
219 };
220 
221 } // namespace CPyCppyy
222 
223 
224 //- public members -----------------------------------------------------------
226 {
227  fEnclosingScope = scope;
228  fName = CPyCppyy_PyText_FromString(Cppyy::GetDatamemberName(scope, idata).c_str());
229  fOffset = Cppyy::GetDatamemberOffset(scope, idata); // TODO: make lazy
230  fFlags = Cppyy::IsStaticData(scope, idata) ? kIsStaticData : 0;
231 
232  std::vector<dim_t> dims;
233  int ndim = 0; dim_t size = 0;
234  while (0 < (size = Cppyy::GetDimensionSize(scope, idata, ndim))) {
235  ndim += 1;
236  if (size == INT_MAX) // meaning: incomplete array type
237  size = -1;
238  if (ndim == 1) { dims.reserve(4); dims.push_back(0); }
239  dims.push_back(size);
240  }
241  if (ndim) {
242  dims[0] = ndim;
243  fFlags |= kIsArrayType;
244  }
245 
246  std::string fullType = Cppyy::GetDatamemberType(scope, idata);
247  if (Cppyy::IsEnumData(scope, idata)) {
248  fullType = Cppyy::ResolveEnum(fullType); // enum might be any type of int
249  fFlags |= kIsConstData;
250  } else if (Cppyy::IsConstData(scope, idata)) {
251  fFlags |= kIsConstData;
252  }
253 
254  fConverter = CreateConverter(fullType, dims.empty() ? nullptr : dims.data());
255 }
256 
257 //-----------------------------------------------------------------------------
258 void CPyCppyy::CPPDataMember::Set(Cppyy::TCppScope_t scope, const std::string& name, void* address)
259 {
260  fEnclosingScope = scope;
261  fName = CPyCppyy_PyText_FromString(name.c_str());
262  fOffset = (intptr_t)address;
264  fConverter = CreateConverter("internal_enum_type_t");
265 }
266 
267 
268 //-----------------------------------------------------------------------------
270 {
271 // class attributes, global properties
272  if (fFlags & kIsStaticData)
273  return (void*)fOffset;
274 
275 // special case: non-static lookup through class
276  if (!pyobj) {
277  PyErr_SetString(PyExc_AttributeError, "attribute access requires an instance");
278  return nullptr;
279  }
280 
281 // instance attributes; requires valid object for full address
282  if (!CPPInstance_Check(pyobj)) {
283  PyErr_Format(PyExc_TypeError,
284  "object instance required for access to property \"%s\"", GetName().c_str());
285  return nullptr;
286  }
287 
288  void* obj = pyobj->GetObject();
289  if (!obj) {
290  PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
291  return nullptr;
292  }
293 
294 // the proxy's internal offset is calculated from the enclosing class
295  ptrdiff_t offset = 0;
296  if (pyobj->ObjectIsA() != fEnclosingScope)
297  offset = Cppyy::GetBaseOffset(pyobj->ObjectIsA(), fEnclosingScope, obj, 1 /* up-cast */);
298 
299  return (void*)((intptr_t)obj + offset + fOffset);
300 }
static CPPDataMember * pp_new(PyTypeObject *pytype, PyObject *, PyObject *)
bool CPPInstance_Check(T *object)
Definition: CPPInstance.h:118
size_t TCppScope_t
Definition: cpp_cppyy.h:18
RPY_EXPORTED std::string GetDatamemberName(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED std::string GetDatamemberType(TCppScope_t scope, TCppIndex_t idata)
Converter * fConverter
PyObject * gLifeLine
Definition: PyStrings.cxx:23
STL namespace.
#define Py_TYPE(ob)
Definition: CPyCppyy.h:209
std::vector< std::pair< ptrdiff_t, PyObject * > > CI_DatamemberCache_t
Definition: CPPInstance.h:24
#define PyVarObject_HEAD_INIT(type, size)
Definition: CPyCppyy.h:207
unsigned int fFlags
CI_DatamemberCache_t & GetDatamemberCache()
size_t TCppIndex_t
Definition: cpp_cppyy.h:24
static int pp_set(CPPDataMember *pyprop, CPPInstance *pyobj, PyObject *value)
virtual PyObject * FromMemory(void *address)
Definition: Converters.cxx:422
virtual bool ToMemory(PyObject *value, void *address)
Definition: Converters.cxx:430
RPY_EXPORTED ptrdiff_t GetBaseOffset(TCppType_t derived, TCppType_t base, TCppObject_t address, int direction, bool rerror=false)
_object PyObject
Definition: PyMethodBase.h:41
PyObject_HEAD intptr_t fOffset
Definition: CPPDataMember.h:25
RPY_EXPORTED bool IsStaticData(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED bool IsEnumData(TCppScope_t scope, TCppIndex_t idata)
PyTypeObject CPPDataMember_Type
#define CPyCppyy_PyText_FromString
Definition: CPyCppyy.h:102
void * GetAddress(CPPInstance *pyobj)
CPYCPPYY_EXTERN Converter * CreateConverter(const std::string &name, Py_ssize_t *dims=nullptr)
RPY_EXPORTED std::string ResolveEnum(const std::string &enum_type)
virtual bool HasState()
Definition: API.h:105
static PyObject * pp_get(CPPDataMember *pyprop, CPPInstance *pyobj, PyObject *)
void Set(Cppyy::TCppScope_t scope, Cppyy::TCppIndex_t idata)
Cppyy::TCppScope_t fEnclosingScope
Definition: CPPDataMember.h:28
RPY_EXPORTED bool IsConstData(TCppScope_t scope, TCppIndex_t idata)
static void pp_dealloc(CPPDataMember *pyprop)
RPY_EXPORTED int GetDimensionSize(TCppScope_t scope, TCppIndex_t idata, int dimension)
Cppyy::TCppType_t ObjectIsA(bool check_smart=true) const
Definition: CPPInstance.h:106
RPY_EXPORTED intptr_t GetDatamemberOffset(TCppScope_t scope, TCppIndex_t idata)
char name[80]
Definition: TGX11.cxx:109
bool LowLevelView_CheckExact(T *object)
Definition: LowLevelViews.h:71