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//----------------------------------------------------------------------------
58{
59
60// setup as necessary
61 if (fArgsRequired == -1 && !this->Initialize(ctxt))
62 return nullptr; // important: 0, not Py_None
63
64// fetch self, verify, and put the arguments in usable order
66 if (!this->ProcessArgs(cargs))
67 return nullptr;
68
69// verify existence of self (i.e. tp_new called)
70 if (!self) {
71 PyErr_SetString(PyExc_ReferenceError, "no python object allocated");
72 return nullptr;
73 }
74
75 if (self->GetObject()) {
77 "object already constructed; use __assign__ instead of __init__");
78 return nullptr;
79 }
80
81 const auto cppScopeFlags = ((CPPScope*)Py_TYPE(self))->fFlags;
82
83// Do nothing if the constructor is explicit and we are in an implicit
84// conversion context. We recognize this by checking the CPPScope::kNoImplicit
85// flag, as further implicit conversions are disabled to prevent infinite
86// recursion. See also the ConvertImplicit() helper in Converters.cxx.
88 return nullptr;
89
90// self provides the python context for lifelines
91 if (!ctxt->fPyContext)
92 ctxt->fPyContext = (PyObject*)cargs.fSelf; // no Py_INCREF as no ownership
93
94// perform the call, nullptr 'this' makes the other side allocate the memory
95 Cppyy::TCppScope_t disp = self->ObjectIsA(false /* check_smart */);
96 intptr_t address = 0;
97 if (GetScope() != disp) {
98 // happens for Python derived types (which have a dispatcher inserted that
99 // is not otherwise user-visible: call it instead) and C++ derived classes
100 // without public constructors
101
102 // first, check whether we at least had a proper meta class, or whether that
103 // was also replaced user-side
104 if (!GetScope() || !disp) {
105 PyErr_SetString(PyExc_TypeError, "can not construct incomplete C++ class");
106 return nullptr;
107 }
108
109 // get the dispatcher class and verify
111 if (!dispproxy) {
112 PyErr_SetString(PyExc_TypeError, "dispatcher proxy was never created");
113 return nullptr;
114 }
115
117 PyErr_SetString(PyExc_TypeError, const_cast<char*>((
118 "constructor for " + Cppyy::GetScopedFinalName(disp) + " is not a dispatcher").c_str()));
119 return nullptr;
120 }
121
123 if (!pyobj)
124 return nullptr;
125
126 // retrieve the actual pointer, take over control, and set set _internal_self
127 address = (intptr_t)((CPPInstance*)pyobj)->GetObject();
128 if (address) {
129 ((CPPInstance*)pyobj)->CppOwns(); // b/c self will control the object on address
132 Py_XDECREF(res);
133 }
135
136 } else {
137 // translate the arguments
140 if (!this->ConvertAndSetArgs(cargs.fArgs, cargs.fNArgsf, ctxt))
141 return nullptr;
142
143 address = (intptr_t)this->Execute(nullptr, 0, ctxt);
144 }
145
146// return object if successful, lament if not
147 if (address) {
149
150 // note: constructors are no longer set to take ownership by default; instead that is
151 // decided by the method proxy (which carries a creator flag) upon return
152 self->Set((void*)address);
153
154 // mark as actual to prevent needless auto-casting and register on its class
155 self->fFlags |= CPPInstance::kIsActual;
158
159 // handling smart types this way is deeply fugly, but if CPPInstance sets the proper
160 // types in op_new first, then the wrong init is called
163 if (pyclass) {
164 self->SetSmart((PyObject*)Py_TYPE(self));
167 }
168 }
169
170 // done with self
172
173 Py_RETURN_NONE; // by definition
174 }
175
176 if (!PyErr_Occurred()) // should be set, otherwise write a generic error msg
177 PyErr_SetString(PyExc_TypeError, const_cast<char*>(
178 (Cppyy::GetScopedFinalName(GetScope()) + " constructor failed").c_str()));
179
180// do not throw an exception, nullptr might trigger the overload handler to
181// choose a different constructor, which if all fails will throw an exception
182 return nullptr;
183}
184
185//----------------------------------------------------------------------------
191
192//----------------------------------------------------------------------------
197
198//----------------------------------------------------------------------------
200{
201 if (this != &s) {
203 fNumBases = s.fNumBases;
204 }
205 return *this;
206}
207
208//----------------------------------------------------------------------------
211{
212// By convention, initialization parameters of multiple base classes are grouped
213// by target base class. Here, we disambiguate and put in "sentinel" parameters
214// that allow the dispatcher to propagate them.
215
216// Three options supported:
217// 0. empty args: default constructor call
218// 1. fNumBases tuples, each handed to individual constructors
219// 2. less than fNumBases, assuming (void) for the missing base constructors
220// 3. normal arguments, going to the first base only
221
222// TODO: this way of forwarding is expensive as the loop is external to this call;
223// it would be more efficient to have the argument handling happen beforehand
224
225#if PY_VERSION_HEX >= 0x03080000
226// fetch self, verify, and put the arguments in usable order (if self is not handled
227// first, arguments can not be reordered with sentinels in place)
229 if (!this->ProcessArgs(cargs))
230 return nullptr;
231
232// to re-use the argument handling, simply change the argument array into a tuple (the
233// benefits of not allocating the tuple are relatively minor in this case)
235 PyObject* args = PyTuple_New(nargs);
236 for (Py_ssize_t i = 0; i < nargs; ++i) {
237 Py_INCREF(cargs.fArgs[i]);
238 PyTuple_SET_ITEM(args, i, cargs.fArgs[i]);
239 }
240
241// copy out self as it may have been updated
242 self = cargs.fSelf;
243
244#else
245 PyObject* args = argsin;
246 Py_INCREF(args);
247#endif
248
249 if (PyTuple_CheckExact(args) && PyTuple_GET_SIZE(args)) { // case 0. falls through
251
252 bool isAllTuples = true;
254 for (Py_ssize_t i = 0; i < nArgs; ++i) {
255 PyObject* argi = PyTuple_GET_ITEM(args, i);
256 if (!PyTuple_CheckExact(argi)) {
257 isAllTuples = false;
258 break;
259 }
261 }
262
263 if (isAllTuples) {
264 // copy over the arguments, while filling in the sentinels (case 1. & 2.), with
265 // just sentinels for the remaining (void) calls (case 2.)
266 PyObject* newArgs = PyTuple_New(nArgsTot + fNumBases - 1);
267 Py_ssize_t idx = 0;
268 for (Py_ssize_t i = 0; i < nArgs; ++i) {
269 if (i != 0) {
270 // add sentinel
273 idx += 1;
274 }
275
276 PyObject* argi = PyTuple_GET_ITEM(args, i);
277 for (Py_ssize_t j = 0; j < PyTuple_GET_SIZE(argi); ++j) {
281 idx += 1;
282 }
283 }
284
285 // add final sentinels as needed
286 while (idx < (nArgsTot+fNumBases-1)) {
289 idx += 1;
290 }
291
292 Py_DECREF(args);
293 args = newArgs;
294 } else { // case 3. add sentinels
295 // copy arguments as-is, then add sentinels at the end
296 PyObject* newArgs = PyTuple_New(PyTuple_GET_SIZE(args) + fNumBases - 1);
297 for (Py_ssize_t i = 0; i < nArgs; ++i) {
298 PyObject* item = PyTuple_GET_ITEM(args, i);
301 }
302 for (Py_ssize_t i = 0; i < fNumBases - 1; ++i) {
305 }
306 Py_DECREF(args);
307 args = newArgs;
308 }
309 }
310
311#if PY_VERSION_HEX < 0x03080000
313#endif
314 nargs = PyTuple_GET_SIZE(args);
315
316#if PY_VERSION_HEX >= 0x03080000
317// now unroll the new args tuple into a vector of objects
318 auto argsu = std::unique_ptr<PyObject*[]>{new PyObject*[nargs]};
319 for (Py_ssize_t i = 0; i < nargs; ++i)
320 argsu[i] = PyTuple_GET_ITEM(args, i);
321 CPyCppyy_PyArgs_t _args = argsu.get();
322#else
323 CPyCppyy_PyArgs_t _args = args;
324#endif
325
327 Py_DECREF(args);
328
329 return result;
330}
331
332
333//----------------------------------------------------------------------------
336{
337// do not allow instantiation of abstract classes
338 if ((self && GetScope() != self->ObjectIsA()
339#if PY_VERSION_HEX >= 0x03080000
340 ) || (!self && !(ctxt->fFlags & CallContext::kFromDescr) && \
342 GetScope() != ((CPPInstance*)args[0])->ObjectIsA()
343#endif
344 )) {
345 // happens if a dispatcher is inserted; allow constructor call
346 return CPPConstructor::Call(self, args, nargsf, kwds, ctxt);
347 }
348
349 PyErr_Format(PyExc_TypeError, "cannot instantiate abstract class \'%s\'"
350 " (from derived classes, use super() instead)",
351 Cppyy::GetScopedFinalName(this->GetScope()).c_str());
352 return nullptr;
353}
354
355
356//----------------------------------------------------------------------------
359{
360// do not allow instantiation of namespaces
361 PyErr_Format(PyExc_TypeError, "cannot instantiate namespace \'%s\'",
362 Cppyy::GetScopedFinalName(this->GetScope()).c_str());
363 return nullptr;
364}
365
366
367//----------------------------------------------------------------------------
370{
371// do not allow instantiation of incomplete (forward declared) classes)
372 PyErr_Format(PyExc_TypeError, "cannot instantiate incomplete class \'%s\'",
373 Cppyy::GetScopedFinalName(this->GetScope()).c_str());
374 return nullptr;
375}
376
377//----------------------------------------------------------------------------
380{
381// do not allow instantiation of classes with only private constructors
382 PyErr_Format(PyExc_TypeError, "cannot instantiate class \'%s\' that has no public constructors",
383 Cppyy::GetScopedFinalName(this->GetScope()).c_str());
384 return nullptr;
385}
#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:376
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
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
PyObject * Call(CPPInstance *&self, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
PyObject * Reflex(Cppyy::Reflex::RequestId_t, Cppyy::Reflex::FormatId_t=Cppyy::Reflex::OPTIMAL) override
bool InitExecutor_(Executor *&, CallContext *ctxt=nullptr) override
PyObject * GetDocString() override
PyObject * Call(CPPInstance *&self, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
PyObject * Call(CPPInstance *&self, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
CPPMethod & operator=(const CPPMethod &)
CPPMultiConstructor(Cppyy::TCppScope_t scope, Cppyy::TCppMethod_t method)
CPPMultiConstructor & operator=(const CPPMultiConstructor &)
PyObject * Call(CPPInstance *&self, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
PyObject * Call(CPPInstance *&self, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject *kwds, CallContext *ctxt=nullptr) override
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:26
PyObject * gDispInit
Definition PyStrings.cxx:69
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)
RPY_EXPORTED bool IsExplicit(TCppMethod_t method)