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