Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
CPPDataMember.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "CPPDataMember.h"
6#include "CPPEnum.h"
7#include "CPPInstance.h"
8#include "Dimensions.h"
9#include "LowLevelViews.h"
10#include "ProxyWrappers.h"
11#include "PyStrings.h"
12#include "TypeManip.h"
13#include "Utility.h"
14#include "cppjit_interop.h"
15#include "cpyrt/Reflex.h"
16
17// Standard
18#include <algorithm>
19#include <limits.h>
20#include <structmember.h>
21#include <vector>
22
23namespace cppjit::cpyrt {
24
26 kNone = 0x0000,
27 kIsStaticData = 0x0001,
28 kIsConstData = 0x0002,
29 kIsArrayType = 0x0004,
30 kIsEnumPrep = 0x0008,
31 kIsEnumType = 0x0010,
32 kIsCachable = 0x0020
33};
34
35//= cpyrt data member as Python property behavior =========================
37 PyObject* /* kls */) {
38 // cache lookup for low level views
39 if (pyobj && dm->fFlags & kIsCachable) {
41 for (auto it = cache.begin(); it != cache.end(); ++it) {
42 if (it->first == dm->fOffset) {
43 if (it->second) {
44 Py_INCREF(it->second);
45 return it->second;
46 } else
47 cache.erase(it);
48 break;
49 }
50 }
51 }
52
53 if (dm->fFlags & (kIsEnumPrep | kIsEnumType)) {
54 if (dm->fFlags & kIsEnumPrep) {
55 // still need to do lookup; only ever try this once, then fallback on
56 // converter
57 dm->fFlags &= ~kIsEnumPrep;
58
59 // fDescription contains the full name of the actual enum value object
60 const interop::TCppScope_t enum_type =
62 const interop::TCppScope_t enum_scope =
63 interop::GetParentScope(enum_type);
64
65 PyObject* pyscope = CreateScopeProxy(enum_scope);
66 if (pyscope) {
67 PyObject* pyEnumType = PyObject_GetAttrString(
68 pyscope, interop::GetFinalName(enum_type).c_str());
69 if (pyEnumType) {
70 PyObject* pyval = PyObject_GetAttrString(
71 pyEnumType, interop::GetFinalName(dm->fScope).c_str());
72 Py_DECREF(pyEnumType);
73 if (pyval) {
74 Py_DECREF(dm->fDescription);
75 dm->fDescription = pyval;
76 dm->fFlags |= kIsEnumType;
77 }
78 }
79 Py_DECREF(pyscope);
80 }
81 if (!(dm->fFlags & kIsEnumType))
82 PyErr_Clear();
83 }
84
85 if (dm->fFlags & kIsEnumType) {
86 Py_INCREF(dm->fDescription);
87 return dm->fDescription;
88 }
89
91 // anonymous enum; cache the value in fDescription like the named case
92 // above: once kIsEnumPrep is cleared this block is not reached again
94 nullptr, nullptr, dm->fScope);
95 if (pyval) {
96 Py_DECREF(dm->fDescription);
97 dm->fDescription = pyval;
98 dm->fFlags |= kIsEnumType;
99 Py_INCREF(pyval);
100 return pyval;
101 }
102 }
103 }
104 // non-initialized or public data accesses through class (e.g. by help())
105 void* address = dm->GetAddress(pyobj);
106 if (!address || (intptr_t)address == -1 /* Cling error */)
107 return nullptr;
108
109 if (dm->fConverter != 0) {
111 (dm->fFlags & kIsArrayType) ? &address : address);
112 if (!result)
113 return result;
114
115 // low level views are expensive to create, so cache them on the object
116 // instead
117 bool isLLView = LowLevelView_CheckExact(result);
118 if (isLLView && CPPInstance_Check(pyobj)) {
119 Py_INCREF(result);
120 pyobj->GetDatamemberCache().push_back(
121 std::make_pair(dm->fOffset, result));
122 dm->fFlags |= kIsCachable;
123 }
124
125 // ensure that the encapsulating class does not go away for the duration
126 // of the data member's lifetime, if it is a bound type (it doesn't matter
127 // for builtin types, b/c those are copied over into python types and thus
128 // end up being "stand-alone")
129 // TODO: should be done for LLViews as well
130 else if (pyobj && !(dm->fFlags & kIsStaticData) &&
132 if (PyObject_SetAttr(result, PyStrings::gLifeLine, (PyObject*)pyobj) ==
133 -1)
134 PyErr_Clear(); // ignored
135 }
136
137 return result;
138 }
139
140 PyErr_Format(PyExc_NotImplementedError, "no converter available for \"%s\"",
141 dm->GetName().c_str());
142 return nullptr;
143}
144
145//-----------------------------------------------------------------------------
146static int dm_set(CPPDataMember* dm, CPPInstance* pyobj, PyObject* value) {
147 // Set the value of the C++ datum held.
148 const int errret = -1;
149
150 if (!value) {
151 // we're being deleted; fine for namespaces (redo lookup next time), but
152 // makes no sense for classes/structs
154 PyErr_SetString(PyExc_TypeError, "data member deletion is not supported");
155 return errret;
156 }
157
158 // deletion removes the proxy, with the idea that a fresh lookup can be
159 // made, to support Cling's shadowing of declarations (TODO: the use case
160 // here is redeclared variables, for which fDescription is indeed th ename;
161 // it might fail for enums).
162 return PyObject_DelAttr((PyObject*)Py_TYPE(pyobj), dm->fDescription);
163 }
164
165 // filter const objects to prevent changing their values
166 if (dm->fFlags & kIsConstData) {
167 PyErr_SetString(PyExc_TypeError, "assignment to const data not allowed");
168 return errret;
169 }
170
171 // remove cached low level view, if any (will be restored upon reaeding)
172 if (dm->fFlags & kIsCachable) {
174 for (auto it = cache.begin(); it != cache.end(); ++it) {
175 if (it->first == dm->fOffset) {
176 Py_XDECREF(it->second);
177 cache.erase(it);
178 break;
179 }
180 }
181 }
182
183 intptr_t address = (intptr_t)dm->GetAddress(pyobj);
184 if (!address || address == -1 /* Cling error */)
185 return errret;
186
187 // for fixed size arrays
188 void* ptr = (void*)address;
189 if (dm->fFlags & kIsArrayType)
190 ptr = &address;
191
192 // actual conversion; return on success
193 if (dm->fConverter && dm->fConverter->ToMemory(value, ptr, (PyObject*)pyobj))
194 return 0;
195
196 // set a python error, if not already done
197 if (!PyErr_Occurred())
198 PyErr_SetString(PyExc_RuntimeError,
199 "property type mismatch or assignment not allowed");
200
201 // failure ...
202 return errret;
203}
204
205//= cpyrt data member construction/destruction ===========================
206static CPPDataMember* dm_new(PyTypeObject* pytype, PyObject*, PyObject*) {
207 // Create and initialize a new property descriptor.
208 CPPDataMember* dm = (CPPDataMember*)pytype->tp_alloc(pytype, 0);
209
210 dm->fOffset = 0;
211 dm->fFlags = 0;
212 dm->fConverter = nullptr;
213 dm->fEnclosingScope = nullptr;
214 dm->fDescription = nullptr;
215 dm->fDoc = nullptr;
216
217 new (&dm->fFullType) std::string{};
218
219 return dm;
220}
221
222//----------------------------------------------------------------------------
223static void dm_dealloc(CPPDataMember* dm) {
224 // Deallocate memory held by this descriptor.
225 using namespace std;
226 if (dm->fConverter && dm->fConverter->HasState())
227 delete dm->fConverter;
228 Py_XDECREF(dm->fDescription); // never exposed so no GC necessary
229 Py_XDECREF(dm->fDoc);
230
231 dm->fFullType.~string();
232
233 Py_TYPE(dm)->tp_free((PyObject*)dm);
234}
235
236static PyMemberDef dm_members[] = {
237 {(char*)"__doc__", T_OBJECT, offsetof(CPPDataMember, fDoc), 0,
238 (char*)"writable documentation"},
239 {NULL, 0, 0, 0, nullptr} /* Sentinel */
240};
241
242//= cpyrt datamember proxy access to internals ============================
244 // Provide the requested reflection information.
245 interop::Reflex::RequestId_t request = -1;
247 if (!PyArg_ParseTuple(args, const_cast<char*>("i|i:__cpp_reflex__"), &request,
248 &format))
249 return nullptr;
250
251 if (request == interop::Reflex::TYPE) {
254 return PyUnicode_FromString(dm->fFullType.c_str());
255 } else if (request == interop::Reflex::OFFSET) {
257 return PyLong_FromLong(dm->fOffset);
258 }
259
260 PyErr_Format(PyExc_ValueError, "unsupported reflex request %d or format %d",
261 request, format);
262 return nullptr;
263}
264
265//----------------------------------------------------------------------------
266static PyMethodDef dm_methods[] = {
267 {(char*)"__cpp_reflex__", (PyCFunction)dm_reflex, METH_VARARGS,
268 (char*)"C++ datamember reflection information"},
269 {(char*)nullptr, nullptr, 0, nullptr}};
270
271//= cpyrt data member type ================================================
272PyTypeObject CPPDataMember_Type = {
273 PyVarObject_HEAD_INIT(&PyType_Type,
274 0)(char*) "cppjit.CPPDataMember", // tp_name
275 sizeof(CPPDataMember), // tp_basicsize
276 0, // tp_itemsize
277 (destructor)dm_dealloc, // tp_dealloc
278 0, // tp_vectorcall_offset / tp_print
279 0, // tp_getattr
280 0, // tp_setattr
281 0, // tp_as_async / tp_compare
282 0, // tp_repr
283 0, // tp_as_number
284 0, // tp_as_sequence
285 0, // tp_as_mapping
286 0, // tp_hash
287 0, // tp_call
288 0, // tp_str
289 0, // tp_getattro
290 0, // tp_setattro
291 0, // tp_as_buffer
292 Py_TPFLAGS_DEFAULT, // tp_flags
293 (char*)"cppjit data member (internal)", // tp_doc
294 0, // tp_traverse
295 0, // tp_clear
296 0, // tp_richcompare
297 0, // tp_weaklistoffset
298 0, // tp_iter
299 0, // tp_iternext
300 dm_methods, // tp_methods
301 dm_members, // tp_members
302 0, // tp_getset
303 0, // tp_base
304 0, // tp_dict
305 (descrgetfunc)dm_get, // tp_descr_get
306 (descrsetfunc)dm_set, // tp_descr_set
307 0, // tp_dictoffset
308 0, // tp_init
309 0, // tp_alloc
310 (newfunc)dm_new, // tp_new
311 0, // tp_free
312 0, // tp_is_gc
313 0, // tp_bases
314 0, // tp_mro
315 0, // tp_cache
316 0, // tp_subclasses
317 0, // tp_weaklist
318 0, // tp_del
319 0, // tp_version_tag
320 0, // tp_finalize
321 0 // tp_vectorcall
323
324} // namespace cppjit::cpyrt
325
326//- public members -----------------------------------------------------------
331 } else {
332 fScope = data;
333 }
334
335 fEnclosingScope = scope;
337 fScope, fScope == data
338 ? scope
340 "__cppjit_internal_wrap_g")); // XXX: Check back here //
341 // TODO: make lazy
343
344 const std::string name = interop::GetFinalName(fScope);
346
350 if (fFullType.find("(anonymous)") == std::string::npos &&
351 fFullType.find("(unnamed)") == std::string::npos) {
352 // repurpose fDescription for lazy lookup of the enum later
353 fDescription = PyUnicode_FromString((fFullType + "::" + name).c_str());
355 }
358 } else {
361
362 // Get the integer type if it's an enum
365
368 }
369
370 auto ldims = interop::GetDimensions(type);
371 std::vector<dim_t> dims(ldims.begin(), ldims.end());
372
373 if (!dims.empty())
375
376 if (dims.empty())
378 else
379 fConverter = CreateConverter(type, {(dim_t)dims.size(), dims.data()});
380
381 if (!(fFlags & kIsEnumPrep))
382 fDescription = PyUnicode_FromString(name.c_str());
383}
384
385//-----------------------------------------------------------------------------
387 const std::string& name, void* address) {
388 fEnclosingScope = scope;
389 fDescription = PyUnicode_FromString(name.c_str());
390 fOffset = (intptr_t)address;
392 fConverter = CreateConverter("internal_enum_type_t");
393 fFullType = "unsigned int";
394}
395
396//-----------------------------------------------------------------------------
398 // class attributes, global properties
399 if (fFlags & kIsStaticData)
400 return (void*)fOffset;
401
402 // special case: non-static lookup through class
403 if (!pyobj) {
404 PyErr_SetString(PyExc_AttributeError,
405 "attribute access requires an instance");
406 return nullptr;
407 }
408
409 // instance attributes; requires valid object for full address
410 if (!CPPInstance_Check(pyobj)) {
411 PyErr_Format(PyExc_TypeError,
412 "object instance required for access to property \"%s\"",
413 GetName().c_str());
414 return nullptr;
415 }
416
417 void* obj = pyobj->GetObject();
418 if (!obj) {
419 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
420 return nullptr;
421 }
422
423 // the proxy's internal offset is calculated from the enclosing class
424 ptrdiff_t offset = 0;
425 interop::TCppScope_t oisa = pyobj->ObjectIsA();
426 if (oisa != fEnclosingScope)
427 offset =
428 interop::GetBaseOffset(oisa, fEnclosingScope, obj, 1 /* up-cast */);
429
430 return (void*)((intptr_t)obj + offset + fOffset);
431}
432
433//-----------------------------------------------------------------------------
435 if (fFlags & kIsEnumType) {
436 PyObject* repr = PyObject_Repr(fDescription);
437 if (repr) {
438 std::string res = PyUnicode_AsUTF8(repr);
439 Py_DECREF(repr);
440 return res;
441 }
442 PyErr_Clear();
443 return "<unknown>";
444 } else if (fFlags & kIsEnumPrep) {
445 std::string fullName = PyUnicode_AsUTF8(fDescription);
446 return fullName.substr(fullName.rfind("::") + 2, std::string::npos);
447 }
448
449 return PyUnicode_AsUTF8(fDescription);
450}
Converter * fConverter
uint32_t fFlags
_object PyObject
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t format
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
Definition TGX11.cxx:142
PyObject_HEAD intptr_t fOffset
void Set(interop::TCppScope_t scope, interop::TCppScope_t var)
interop::TCppScope_t fEnclosingScope
void * GetAddress(CPPInstance *pyobj)
interop::TCppScope_t fScope
CI_DatamemberCache_t & GetDatamemberCache()
virtual PyObject * FromMemory(void *address)
virtual bool HasState()
Definition API.h:133
virtual bool ToMemory(PyObject *value, void *address, PyObject *ctxt=nullptr)
#define CPYRT_PYTYPE_TAIL
Definition cpyrt.h:159
static CPPDataMember * dm_new(PyTypeObject *pytype, PyObject *, PyObject *)
CPYRT_EXTERN Converter * CreateConverter(const std::string &name, cdims_t=0)
PyTypeObject CPPDataMember_Type
static PyObject * dm_reflex(CPPDataMember *dm, PyObject *args)
bool LowLevelView_CheckExact(T *object)
PyObject * pyval_from_enum(const std::string &enum_type, PyObject *pytype, PyObject *btype, interop::TCppScope_t enum_constant)
Definition CPPEnum.cxx:23
static PyObject * dm_get(CPPDataMember *dm, CPPInstance *pyobj, PyObject *)
Py_ssize_t dim_t
Definition API.h:100
PyObject * CreateScopeProxy(PyObject *, PyObject *args)
std::vector< std::pair< ptrdiff_t, PyObject * > > CI_DatamemberCache_t
Definition CPPInstance.h:26
static int dm_set(CPPDataMember *dm, CPPInstance *pyobj, PyObject *value)
bool CPPInstance_Check(T *object)
static PyMemberDef dm_members[]
static void dm_dealloc(CPPDataMember *dm)
static PyMethodDef dm_methods[]
const FormatId_t OPTIMAL
Definition Reflex.h:22
const FormatId_t AS_STRING
Definition Reflex.h:24
const RequestId_t OFFSET
Definition Reflex.h:17
const RequestId_t TYPE
Definition Reflex.h:19
bool IsConstVar(TCppScope_t var)
Cpp::TypeRef TCppType_t
Definition API.h:42
std::string GetTypeAsString(TCppType_t type)
TCppType_t GetDatamemberType(TCppScope_t data)
TCppScope_t WrapLambdaFromVariable(TCppScope_t var)
TCppScope_t GetParentScope(TCppScope_t scope)
bool IsStaticDatamember(TCppScope_t var)
intptr_t GetDatamemberOffset(TCppScope_t var, TCppScope_t klass=nullptr)
TCppType_t ResolveType(TCppType_t cppitem_name)
std::string ResolveEnum(TCppScope_t enum_scope)
bool IsNamespace(TCppScope_t scope)
TCppScope_t GetScope(const std::string &scope_name, TCppScope_t parent_scope=TCppScope_t{})
TCppType_t GetEnumConstantType(TCppScope_t scope)
bool IsEnumConstant(TCppScope_t scope)
std::string GetFinalName(TCppScope_t type)
bool IsLambdaClass(TCppType_t type)
ptrdiff_t GetBaseOffset(TCppScope_t derived, TCppScope_t base, TCppObject_t address, int direction, bool rerror=false)
Cpp::DeclRef TCppScope_t
Definition API.h:41
std::vector< long int > GetDimensions(TCppType_t type)
bool IsEnumType(TCppType_t type)
Definition API.cxx:34