diff --git a/bindings/pyroot_experimental/PyROOT/CMakeLists.txt b/bindings/pyroot_experimental/PyROOT/CMakeLists.txt index 891f3027ba4d46fee66adc3e67220528cbe5af7f..556e2f1bce1d67bb14cf36523e2cdb89eceec7bc 100644 --- a/bindings/pyroot_experimental/PyROOT/CMakeLists.txt +++ b/bindings/pyroot_experimental/PyROOT/CMakeLists.txt @@ -26,14 +26,15 @@ set(sources src/PyROOTModule.cxx src/PyROOTStrings.cxx src/PyROOTWrapper.cxx + src/GenericPyz.cxx + src/RVecPyz.cxx + src/TClassPyz.cxx + src/TClonesArrayPyz.cxx src/TDirectoryPyz.cxx src/TFilePyz.cxx src/TTreePyz.cxx - src/TClonesArrayPyz.cxx - src/GenericPyz.cxx - src/RVecPyz.cxx - src/PyzPythonHelpers.cxx src/PyzCppHelpers.cxx + src/PyzPythonHelpers.cxx ) file(COPY python/ROOT DESTINATION ${localruntimedir}) diff --git a/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_tclass.py b/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_tclass.py new file mode 100644 index 0000000000000000000000000000000000000000..7f716689b410505ad91f25c4b3ead7b7fbe8a6ff --- /dev/null +++ b/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_tclass.py @@ -0,0 +1,23 @@ +# Author: Enric Tejedor CERN 02/2019 + +################################################################################ +# Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. # +# All rights reserved. # +# # +# For the licensing terms see $ROOTSYS/LICENSE. # +# For the list of contributors see $ROOTSYS/README/CREDITS. # +################################################################################ + +from ROOT import pythonization +import cppyy +from libROOTPython import AddTClassDynamicCastPyz + + +@pythonization(lazy = False) +def pythonize_tclass(): + klass = cppyy.gbl.TClass + + # DynamicCast + AddTClassDynamicCastPyz(klass) + + return True diff --git a/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx b/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx index 141722ac7593adbb50f07a492a9416350cd78ded..060fbdc1108322f8054d666d4bdb3acea289d7b1 100644 --- a/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx +++ b/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx @@ -45,6 +45,8 @@ static PyMethodDef gPyROOTMethods[] = {{(char *)"AddDirectoryWritePyz", (PyCFunc (char *)"Allow to access branches as tree attributes"}, {(char *)"AddFileOpenPyz", (PyCFunction)PyROOT::AddFileOpenPyz, METH_VARARGS, (char *)"Make TFile::Open a constructor, adjusting for example the reference count"}, + {(char *)"AddTClassDynamicCastPyz", (PyCFunction)PyROOT::AddTClassDynamicCastPyz, METH_VARARGS, + (char *)"Cast the void* returned by TClass::DynamicCast to the right type"}, {(char *)"SetBranchAddressPyz", (PyCFunction)PyROOT::SetBranchAddressPyz, METH_VARARGS, (char *)"Fully enable the use of TTree::SetBranchAddress from Python"}, {(char *)"BranchPyz", (PyCFunction)PyROOT::BranchPyz, METH_VARARGS, diff --git a/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h b/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h index ec7761a51ade461bf7825cde004eb2eea5a68e4d..344881370c3ef0203623bfd50124ab937fb536c4 100644 --- a/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h +++ b/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h @@ -17,17 +17,24 @@ namespace PyROOT { PyObject *AddPrettyPrintingPyz(PyObject *self, PyObject *args); -PyObject *AddDirectoryWritePyz(PyObject *self, PyObject *args); + PyObject *AddDirectoryAttrSyntaxPyz(PyObject *self, PyObject *args); -PyObject *AddBranchAttrSyntax(PyObject *self, PyObject *args); +PyObject *AddDirectoryWritePyz(PyObject *self, PyObject *args); PyObject *AddFileOpenPyz(PyObject *self, PyObject *args); -PyObject *AddSetItemTCAPyz(PyObject *self, PyObject *args); -PyObject *SetBranchAddressPyz(PyObject *self, PyObject *args); + +PyObject *AddBranchAttrSyntax(PyObject *self, PyObject *args); PyObject *BranchPyz(PyObject *self, PyObject *args); +PyObject *SetBranchAddressPyz(PyObject *self, PyObject *args); + +PyObject *AddTClassDynamicCastPyz(PyObject *self, PyObject *args); + +PyObject *AddSetItemTCAPyz(PyObject *self, PyObject *args); + +PyObject *AsRVec(PyObject *self, PyObject *obj); + PyObject *GetEndianess(PyObject *self); PyObject *GetVectorDataPointer(PyObject *self, PyObject *args); PyObject *GetSizeOfType(PyObject *self, PyObject *args); -PyObject *AsRVec(PyObject *self, PyObject *obj); } // namespace PyROOT diff --git a/bindings/pyroot_experimental/PyROOT/src/TClassPyz.cxx b/bindings/pyroot_experimental/PyROOT/src/TClassPyz.cxx new file mode 100644 index 0000000000000000000000000000000000000000..886522e47d8c0808abe77d546e1a8b656ee82ce1 --- /dev/null +++ b/bindings/pyroot_experimental/PyROOT/src/TClassPyz.cxx @@ -0,0 +1,95 @@ +// Author: Enric Tejedor CERN 02/2019 +// Original PyROOT code by Wim Lavrijsen, LBL + +/************************************************************************* + * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +// Bindings +#include "CPyCppyy.h" +#include "PyROOTPythonize.h" +#include "PyROOTStrings.h" +#include "CPPInstance.h" +#include "Utility.h" +#include "ProxyWrappers.h" +#include "PyzCppHelpers.hxx" + +// ROOT +#include "TClass.h" + +using namespace CPyCppyy; + +// Cast the void* returned by TClass::DynamicCast to the right type +PyObject *TClassDynamicCastPyz(CPPInstance *self, PyObject *args) +{ + // Parse arguments + CPPInstance *pyclass = nullptr; + PyObject *pyobject = nullptr; + int up = 1; + if (!PyArg_ParseTuple(args, const_cast<char *>("O!O|i:DynamicCast"), + &CPPInstance_Type, &pyclass, + &pyobject, + &up)) + return nullptr; + + // Perform actual cast - calls default implementation of DynamicCast + auto meth = PyObject_GetAttr((PyObject *)self, PyROOT::PyStrings::gTClassDynCast); + auto ptr = meth ? PyObject_Call(meth, args, nullptr) : nullptr; + Py_XDECREF(meth); + + // Simply forward in case of call failure + if (!ptr) + return nullptr; + + // Retrieve object address + void *address = nullptr; + if (CPPInstance_Check(pyobject)) { + address = ((CPPInstance *)pyobject)->GetObject(); + } else if (PyInt_Check(pyobject) || PyLong_Check(pyobject)) { + address = (void *)PyLong_AsLong(pyobject); + } else { + Utility::GetBuffer(pyobject, '*', 1, address, false); + } + + if (PyErr_Occurred()) { + // Error getting object address, just return the void* wrapper + PyErr_Clear(); + return ptr; + } + + // Now use binding to return a usable class + TClass *klass = nullptr; + if (up) { + // Upcast: result is a base + klass = (TClass *)GetTClass(pyclass)->DynamicCast(TClass::Class(), pyclass->GetObject()); + } else { + // Downcast: result is a derived + klass = (TClass *)GetTClass(self)->DynamicCast(TClass::Class(), self->GetObject()); + } + + PyObject *result = BindCppObjectNoCast(address, Cppyy::GetScope(klass->GetName())); + Py_DECREF(ptr); + + return result; +} + +//////////////////////////////////////////////////////////////////////////// +/// \brief Add pythonization for TClass::DynamicCast. +/// \param[in] self Always null, since this is a module function. +/// \param[in] args Pointer to a Python tuple object containing the arguments +/// received from Python. +/// +/// TClass::DynamicCast returns a void* that the user still has to cast (it +/// will have the proper offset, though). Fix this by providing the requested +/// binding if the cast succeeded. +PyObject *PyROOT::AddTClassDynamicCastPyz(PyObject * /* self */, PyObject *args) +{ + PyObject *pyclass = PyTuple_GetItem(args, 0); + Utility::AddToClass(pyclass, "_TClass__DynamicCast", "DynamicCast"); + Utility::AddToClass(pyclass, "DynamicCast", (PyCFunction)TClassDynamicCastPyz); + Py_RETURN_NONE; +} \ No newline at end of file