Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
CPPConstructor.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "CPPConstructor.h"
6#include "CPPInstance.h"
7#include "Executors.h"
8#include "MemoryRegulator.h"
9#include "ProxyWrappers.h"
10#include "PyStrings.h"
11
12// Standard
13#include <memory>
14#include <string>
15
16//- data _____________________________________________________________________
17namespace cppjit::cpyrt {
19void* Instance_AsVoidPtr(PyObject* pyobject);
21 bool python_owns);
22} // namespace cppjit::cpyrt
23
24//- protected members --------------------------------------------------------
26 // pick up special case new object executor
27 executor = CreateExecutor("__init__");
28 return true;
29}
30
31//- public members -----------------------------------------------------------
33 // GetMethod() may return an empty function if this is just a special case
34 // place holder
35 const std::string& clName = interop::GetFinalName(this->GetScope());
36 return PyUnicode_FromFormat(
37 "%s::%s%s", clName.c_str(), clName.c_str(),
38 this->GetMethod() ? this->GetSignatureString().c_str() : "()");
39}
40
41//----------------------------------------------------------------------------
44 // C++ reflection tooling for constructors.
45
46 if (request == interop::Reflex::RETURN_TYPE) {
47 std::string fn = interop::GetScopedFinalName(this->GetScope());
50 return CreateScopeProxy(this->GetScope());
52 return PyUnicode_FromString(fn.c_str());
53 }
54
55 return PyCallable::Reflex(request, format);
56}
57
58//----------------------------------------------------------------------------
60 size_t nargsf, PyObject* kwds,
61 CallContext* ctxt) {
62 // setup as necessary
63 if (fArgsRequired == -1 && !this->Initialize(ctxt))
64 return nullptr; // important: 0, not Py_None
65
66 // fetch self, verify, and put the arguments in usable order
67 PyCallArgs cargs{self, args, nargsf, kwds};
68 if (!this->ProcessArgs(cargs))
69 return nullptr;
70
71 // verify existence of self (i.e. tp_new called)
72 if (!self) {
73 PyErr_SetString(PyExc_ReferenceError, "no python object allocated");
74 return nullptr;
75 }
76
77 if (self->GetObject()) {
78 PyErr_SetString(
79 PyExc_ReferenceError,
80 "object already constructed; use __assign__ instead of __init__");
81 return nullptr;
82 }
83
84 const auto cppScopeFlags = ((CPPScope*)Py_TYPE(self))->fFlags;
85
86 // Do nothing if the constructor is explicit and we are in an implicit
87 // conversion context. See also the ConvertImplicit() helper in
88 // Converters.cxx.
89 if ((cppScopeFlags & CPPScope::kActiveImplicitCall) &&
90 interop::IsExplicit(GetMethod())) {
91 // FIXME: Cases with explicit marked std::complex constructors where we
92 // expect implicit conversionss
93 if (interop::GetMethodSignature(GetMethod(), true).find("std::complex") ==
94 std::string::npos)
95 return nullptr;
96 }
97
98 // self provides the python context for lifelines
99 if (!ctxt->fPyContext)
100 ctxt->fPyContext = (PyObject*)cargs.fSelf; // no Py_INCREF as no ownership
101
102 // perform the call, nullptr 'this' makes the other side allocate the memory
103 interop::TCppScope_t disp = self->ObjectIsA(false /* check_smart */);
104 intptr_t address = 0;
105 if (GetScope() != disp) {
106 // happens for Python derived types (which have a dispatcher inserted that
107 // is not otherwise user-visible: call it instead) and C++ derived classes
108 // without public constructors
109
110 // first, check whether we at least had a proper meta class, or whether that
111 // was also replaced user-side
112 if (!GetScope() || !disp) {
113 PyErr_SetString(PyExc_TypeError,
114 "can not construct incomplete C++ class");
115 return nullptr;
116 }
117
118 // get the dispatcher class and verify
119 PyObject* dispproxy = cpyrt::GetScopeProxy(disp);
120 if (!dispproxy) {
121 PyErr_SetString(PyExc_TypeError, "dispatcher proxy was never created");
122 return nullptr;
123 }
124
125 if (!(((CPPClass*)dispproxy)->fFlags & CPPScope::kIsPython)) {
126 PyErr_SetString(PyExc_TypeError,
127 const_cast<char*>(("constructor for " +
129 " is not a dispatcher")
130 .c_str()));
131 return nullptr;
132 }
133
134 PyObject* pyobj =
135 cpyrt_PyObject_Call(dispproxy, cargs.fArgs, cargs.fNArgsf, kwds);
136 if (!pyobj)
137 return nullptr;
138
139 // retrieve the actual pointer, take over control, and set set
140 // _internal_self
141 address = (intptr_t)((CPPInstance*)pyobj)->GetObject();
142 if (address) {
143 ((CPPInstance*)pyobj)
144 ->CppOwns(); // b/c self will control the object on address
145 PyObject* res = PyObject_CallMethodObjArgs(
146 dispproxy, PyStrings::gDispInit, pyobj, (PyObject*)self, nullptr);
147 Py_XDECREF(res);
148 }
149 Py_DECREF(dispproxy);
150
151 } else {
152 // translate the arguments
153 if (cppScopeFlags & CPPScope::kActiveImplicitCall)
155 if (!this->ConvertAndSetArgs(cargs.fArgs, cargs.fNArgsf, ctxt))
156 return nullptr;
157
158 address = (intptr_t)this->Execute(nullptr, 0, ctxt);
159 }
160
161 // return object if successful, lament if not
162 if (address) {
163 Py_INCREF(self);
164
165 // note: constructors are no longer set to take ownership by default;
166 // instead that is decided by the method proxy (which carries a creator
167 // flag) upon return
168 self->Set((void*)address);
169
170 // mark as actual to prevent needless auto-casting and register on its class
172 if (!(((CPPClass*)Py_TYPE(self))->fFlags & CPPScope::kIsSmart))
174 interop::TCppObject_t((void*)address));
175
176 // handling smart types this way is deeply fugly, but if CPPInstance sets
177 // the proper types in op_new first, then the wrong init is called
178 if (((CPPClass*)Py_TYPE(self))->fFlags & CPPScope::kIsSmart) {
179 PyObject* pyclass =
181 if (pyclass) {
182 self->SetSmart((PyObject*)Py_TYPE(self));
183 Py_DECREF((PyObject*)Py_TYPE(self));
184 Py_SET_TYPE(self, (PyTypeObject*)pyclass);
185 }
186 }
187
188 // done with self
189 Py_DECREF(self);
190
191 Py_RETURN_NONE; // by definition
192 }
193
194 if (!PyErr_Occurred()) // should be set, otherwise write a generic error msg
195 PyErr_SetString(PyExc_TypeError,
196 const_cast<char*>((interop::GetScopedFinalName(GetScope()) +
197 " constructor failed")
198 .c_str()));
199
200 // do not throw an exception, nullptr might trigger the overload handler to
201 // choose a different constructor, which if all fails will throw an exception
202 return nullptr;
203}
204
205//----------------------------------------------------------------------------
211
212//----------------------------------------------------------------------------
215
216//----------------------------------------------------------------------------
219 if (this != &s) {
221 fNumBases = s.fNumBases;
222 }
223 return *this;
224}
225
226//----------------------------------------------------------------------------
228 cpyrt_PyArgs_t argsin, size_t nargsf,
229 PyObject* kwds, CallContext* ctxt) {
230 // By convention, initialization parameters of multiple base classes are
231 // grouped by target base class. Here, we disambiguate and put in "sentinel"
232 // parameters that allow the dispatcher to propagate them.
233
234 // Three options supported:
235 // 0. empty args: default constructor call
236 // 1. fNumBases tuples, each handed to individual constructors
237 // 2. less than fNumBases, assuming (void) for the missing base constructors
238 // 3. normal arguments, going to the first base only
239
240 // TODO: this way of forwarding is expensive as the loop is external to this
241 // call; it would be more efficient to have the argument handling happen
242 // beforehand
243
244 // fetch self, verify, and put the arguments in usable order (if self is not
245 // handled first, arguments can not be reordered with sentinels in place)
246 PyCallArgs cargs{self, argsin, nargsf, kwds};
247 if (!this->ProcessArgs(cargs))
248 return nullptr;
249
250 // to re-use the argument handling, simply change the argument array into a
251 // tuple (the benefits of not allocating the tuple are relatively minor in
252 // this case)
253 Py_ssize_t nargs = cpyrt_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf);
254 PyObject* args = PyTuple_New(nargs);
255 for (Py_ssize_t i = 0; i < nargs; ++i) {
256 Py_INCREF(cargs.fArgs[i]);
257 PyTuple_SET_ITEM(args, i, cargs.fArgs[i]);
258 }
259
260 // copy out self as it may have been updated
261 self = cargs.fSelf;
262
263 if (PyTuple_CheckExact(args) &&
264 PyTuple_GET_SIZE(args)) { // case 0. falls through
265 Py_ssize_t nArgs = PyTuple_GET_SIZE(args);
266
267 bool isAllTuples = true;
268 Py_ssize_t nArgsTot = 0;
269 for (Py_ssize_t i = 0; i < nArgs; ++i) {
270 PyObject* argi = PyTuple_GET_ITEM(args, i);
271 if (!PyTuple_CheckExact(argi)) {
272 isAllTuples = false;
273 break;
274 }
275 nArgsTot += PyTuple_GET_SIZE(argi);
276 }
277
278 if (isAllTuples) {
279 // copy over the arguments, while filling in the sentinels (case 1. & 2.),
280 // with just sentinels for the remaining (void) calls (case 2.)
281 PyObject* newArgs = PyTuple_New(nArgsTot + fNumBases - 1);
282 Py_ssize_t idx = 0;
283 for (Py_ssize_t i = 0; i < nArgs; ++i) {
284 if (i != 0) {
285 // add sentinel
286 Py_INCREF(gNullPtrObject);
287 PyTuple_SET_ITEM(newArgs, idx, gNullPtrObject);
288 idx += 1;
289 }
290
291 PyObject* argi = PyTuple_GET_ITEM(args, i);
292 for (Py_ssize_t j = 0; j < PyTuple_GET_SIZE(argi); ++j) {
293 PyObject* item = PyTuple_GET_ITEM(argi, j);
294 Py_INCREF(item);
295 PyTuple_SET_ITEM(newArgs, idx, item);
296 idx += 1;
297 }
298 }
299
300 // add final sentinels as needed
301 while (idx < (nArgsTot + fNumBases - 1)) {
302 Py_INCREF(gNullPtrObject);
303 PyTuple_SET_ITEM(newArgs, idx, gNullPtrObject);
304 idx += 1;
305 }
306
307 Py_DECREF(args);
308 args = newArgs;
309 } else { // case 3. add sentinels
310 // copy arguments as-is, then add sentinels at the end
311 PyObject* newArgs = PyTuple_New(PyTuple_GET_SIZE(args) + fNumBases - 1);
312 for (Py_ssize_t i = 0; i < nArgs; ++i) {
313 PyObject* item = PyTuple_GET_ITEM(args, i);
314 Py_INCREF(item);
315 PyTuple_SET_ITEM(newArgs, i, item);
316 }
317 for (Py_ssize_t i = 0; i < fNumBases - 1; ++i) {
318 Py_INCREF(gNullPtrObject);
319 PyTuple_SET_ITEM(newArgs, i + nArgs, gNullPtrObject);
320 }
321 Py_DECREF(args);
322 args = newArgs;
323 }
324 }
325
326 nargs = PyTuple_GET_SIZE(args);
327
328 // now unroll the new args tuple into a vector of objects
329 auto argsu = std::unique_ptr<PyObject*[]>{new PyObject*[nargs]};
330 for (Py_ssize_t i = 0; i < nargs; ++i)
331 argsu[i] = PyTuple_GET_ITEM(args, i);
332 cpyrt_PyArgs_t _args = argsu.get();
333
334 PyObject* result = CPPConstructor::Call(self, _args, nargs, kwds, ctxt);
335 Py_DECREF(args);
336
337 return result;
338}
339
340//----------------------------------------------------------------------------
342 cpyrt_PyArgs_t args,
343 size_t nargsf,
344 PyObject* kwds,
345 CallContext* ctxt) {
346 // do not allow instantiation of abstract classes
347 if ((self && GetScope() != self->ObjectIsA()) ||
348 (!self && !(ctxt->fFlags & CallContext::kFromDescr) &&
349 cpyrt_PyArgs_GET_SIZE(args, nargsf) && CPPInstance_Check(args[0]) &&
350 GetScope() != ((CPPInstance*)args[0])->ObjectIsA())) {
351 // happens if a dispatcher is inserted; allow constructor call
352 return CPPConstructor::Call(self, args, nargsf, kwds, ctxt);
353 }
354
355 PyErr_Format(PyExc_TypeError,
356 "cannot instantiate abstract class \'%s\'"
357 " (from derived classes, use super() instead)",
358 interop::GetScopedFinalName(this->GetScope()).c_str());
359 return nullptr;
360}
361
362//----------------------------------------------------------------------------
364 size_t, PyObject*,
365 CallContext*) {
366 // do not allow instantiation of namespaces
367 PyErr_Format(PyExc_TypeError, "cannot instantiate namespace \'%s\'",
368 interop::GetScopedFinalName(this->GetScope()).c_str());
369 return nullptr;
370}
371
372//----------------------------------------------------------------------------
374 cpyrt_PyArgs_t, size_t,
376 // do not allow instantiation of incomplete (forward declared) classes)
377 PyErr_Format(PyExc_TypeError, "cannot instantiate incomplete class \'%s\'",
378 interop::GetScopedFinalName(this->GetScope()).c_str());
379 return nullptr;
380}
381
382//----------------------------------------------------------------------------
384 cpyrt_PyArgs_t, size_t,
386 // do not allow instantiation of classes with only private constructors
387 PyErr_Format(
388 PyExc_TypeError,
389 "cannot instantiate class \'%s\' that has no public constructors",
390 interop::GetScopedFinalName(this->GetScope()).c_str());
391 return nullptr;
392}
interop::TCppScope_t fUnderlyingType
uint32_t fFlags
_object PyObject
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 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
PyObject * Call(CPPInstance *&self, cpyrt_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
PyObject * Call(CPPInstance *&self, cpyrt_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
bool InitExecutor_(Executor *&, CallContext *ctxt=nullptr) override
PyObject * Call(CPPInstance *&self, cpyrt_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
PyObject * GetDocString() override
PyObject * Reflex(interop::Reflex::RequestId_t, interop::Reflex::FormatId_t=interop::Reflex::OPTIMAL) override
PyObject * Call(CPPInstance *&self, cpyrt_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
void SetSmart(PyObject *smart_type)
interop::TCppScope_t ObjectIsA(bool check_smart=true) const
void Set(void *address, EFlags flags=kDefault)
Definition CPPInstance.h:99
CPPMethod & operator=(const CPPMethod &)
PyObject * Call(CPPInstance *&self, cpyrt_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
CPPMultiConstructor(interop::TCppScope_t scope, interop::TCppMethod_t method)
CPPMultiConstructor & operator=(const CPPMultiConstructor &)
PyObject * Call(CPPInstance *&self, cpyrt_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
static bool RegisterPyObject(CPPInstance *pyobj, interop::TCppObject_t cppobj)
virtual PyObject * Reflex(interop::Reflex::RequestId_t request, interop::Reflex::FormatId_t format=interop::Reflex::OPTIMAL)
Definition PyCallable.h:27
PyObject *const * cpyrt_PyArgs_t
Definition cpyrt.h:81
static Py_ssize_t cpyrt_PyArgs_GET_SIZE(cpyrt_PyArgs_t, size_t nargsf)
Definition cpyrt.h:90
#define Py_RETURN_NONE
Definition cpyrt.h:69
#define cpyrt_PyObject_Call
Definition cpyrt.h:99
PyObject * gNullPtrObject
PyObject * GetScopeProxy(interop::TCppScope_t)
PyObject * CreateScopeProxy(PyObject *, PyObject *args)
CPYRT_EXTERN Executor * CreateExecutor(const std::string &name, cdims_t=0)
CPYRT_EXTERN void * Instance_AsVoidPtr(PyObject *pyobject)
Definition API.cxx:105
bool CPPInstance_Check(T *object)
PyObject * Instance_FromVoidPtr(void *addr, const char *classname, int python_owns)
Definition API.cxx:161
const FormatId_t OPTIMAL
Definition Reflex.h:22
const FormatId_t AS_STRING
Definition Reflex.h:24
const RequestId_t RETURN_TYPE
Definition Reflex.h:18
const FormatId_t AS_TYPE
Definition Reflex.h:23
Cpp::ObjectRef TCppObject_t
Definition API.h:43
TCppIndex_t GetNumBases(TCppScope_t klass)
Cpp::FuncRef TCppMethod_t
Definition API.h:44
std::string GetMethodSignature(TCppMethod_t, bool show_formal_args, TCppIndex_t max_args=(TCppIndex_t) -1)
bool IsExplicit(TCppMethod_t method)
std::string GetFinalName(TCppScope_t type)
std::string GetScopedFinalName(TCppScope_t type)
Cpp::DeclRef TCppScope_t
Definition API.h:41
Definition API.cxx:34