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