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
16namespace 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 =========================
27static 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//-----------------------------------------------------------------------------
92static 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, (PyObject*)pyobj))
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 ===========================
137static 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//----------------------------------------------------------------------------
152static 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 ================================================
164PyTypeObject 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;
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;
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
250 } else if (Cppyy::IsConstData(scope, idata)) {
252 }
253
254 fConverter = CreateConverter(fullType, dims.empty() ? nullptr : dims.data());
255}
256
257//-----------------------------------------------------------------------------
258void 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}
#define Py_TYPE(ob)
Definition: CPyCppyy.h:217
Py_ssize_t dim_t
Definition: CPyCppyy.h:64
#define CPyCppyy_PyText_FromString
Definition: CPyCppyy.h:102
#define PyVarObject_HEAD_INIT(type, size)
Definition: CPyCppyy.h:215
Converter * fConverter
unsigned int fFlags
_object PyObject
Definition: PyMethodBase.h:42
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
char name[80]
Definition: TGX11.cxx:110
PyObject_HEAD intptr_t fOffset
Definition: CPPDataMember.h:25
Cppyy::TCppScope_t fEnclosingScope
Definition: CPPDataMember.h:28
void * GetAddress(CPPInstance *pyobj)
void Set(Cppyy::TCppScope_t scope, Cppyy::TCppIndex_t idata)
CI_DatamemberCache_t & GetDatamemberCache()
Cppyy::TCppType_t ObjectIsA(bool check_smart=true) const
Definition: CPPInstance.h:106
virtual bool HasState()
Definition: API.h:105
virtual bool ToMemory(PyObject *value, void *address, PyObject *ctxt=nullptr)
Definition: Converters.cxx:446
virtual PyObject * FromMemory(void *address)
Definition: Converters.cxx:438
PyObject * gLifeLine
Definition: PyStrings.cxx:23
Set of helper functions that are invoked from the pythonizors, on the Python side.
static PyObject * pp_get(CPPDataMember *pyprop, CPPInstance *pyobj, PyObject *)
static int pp_set(CPPDataMember *pyprop, CPPInstance *pyobj, PyObject *value)
static void pp_dealloc(CPPDataMember *pyprop)
std::vector< std::pair< ptrdiff_t, PyObject * > > CI_DatamemberCache_t
Definition: CPPInstance.h:24
bool LowLevelView_CheckExact(T *object)
Definition: LowLevelViews.h:71
bool CPPInstance_Check(T *object)
Definition: CPPInstance.h:118
static CPPDataMember * pp_new(PyTypeObject *pytype, PyObject *, PyObject *)
PyTypeObject CPPDataMember_Type
CPYCPPYY_EXTERN Converter * CreateConverter(const std::string &name, Py_ssize_t *dims=nullptr)
size_t TCppIndex_t
Definition: cpp_cppyy.h:24
RPY_EXPORTED ptrdiff_t GetBaseOffset(TCppType_t derived, TCppType_t base, TCppObject_t address, int direction, bool rerror=false)
RPY_EXPORTED int GetDimensionSize(TCppScope_t scope, TCppIndex_t idata, int dimension)
RPY_EXPORTED intptr_t GetDatamemberOffset(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED std::string GetDatamemberName(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED bool IsEnumData(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED bool IsStaticData(TCppScope_t scope, TCppIndex_t idata)
RPY_EXPORTED bool IsConstData(TCppScope_t scope, TCppIndex_t idata)
size_t TCppScope_t
Definition: cpp_cppyy.h:18
RPY_EXPORTED std::string ResolveEnum(const std::string &enum_type)
RPY_EXPORTED std::string GetDatamemberType(TCppScope_t scope, TCppIndex_t idata)