diff --git a/bindings/distrdf/python/DistRDF/Backends/Base.py b/bindings/distrdf/python/DistRDF/Backends/Base.py index d4bc1da00de9d..8ba45961b32d2 100644 --- a/bindings/distrdf/python/DistRDF/Backends/Base.py +++ b/bindings/distrdf/python/DistRDF/Backends/Base.py @@ -49,7 +49,7 @@ def setup_mapper(initialization_fn: Callable, code_to_declare: str) -> None: initialization_fn() # Declare all user code in one call - ROOT.gInterpreter.Declare(code_to_declare) + ROOT.gCling.Declare(code_to_declare) def get_mergeable_values(starting_node: ROOT.RDF.RNode, range_id: int, @@ -215,7 +215,7 @@ def register_declaration(cls, code_to_declare): code_with_guard = f"#ifndef {hex}\n#define {hex}\n{stripped}\n#endif" cls.strings_to_declare[hex] = code_with_guard - ROOT.gInterpreter.Declare(cls.strings_to_declare[hex]) + ROOT.gCling.Declare(cls.strings_to_declare[hex]) @classmethod def register_shared_lib(cls, paths_to_shared_libraries): diff --git a/bindings/distrdf/python/DistRDF/Backends/Utils.py b/bindings/distrdf/python/DistRDF/Backends/Utils.py index fb2a69950dcd2..6c77a19e4368d 100644 --- a/bindings/distrdf/python/DistRDF/Backends/Utils.py +++ b/bindings/distrdf/python/DistRDF/Backends/Utils.py @@ -39,10 +39,10 @@ def extend_include_path(include_path: str) -> None: needed for the analysis. """ root_path = "-I{}".format(include_path) - ROOT.gInterpreter.AddIncludePath(root_path) + ROOT.gCling.AddIncludePath(root_path) # Retrieve ROOT internal list of include paths and add debug statement - root_includepath = ROOT.gInterpreter.GetIncludePath() + root_includepath = ROOT.gCling.GetIncludePath() logger.debug("ROOT include paths:\n{}".format(root_includepath)) @@ -62,7 +62,7 @@ def distribute_headers(headers_to_include: Iterable[str]) -> None: # Create C++ include code include_code = "#include \"{}\"\n".format(header) try: - ROOT.gInterpreter.Declare(include_code) + ROOT.gCling.Declare(include_code) except Exception as e: msg = "There was an error in including \"{}\" !".format(header) raise e(msg) diff --git a/bindings/distrdf/test/backend/test_common.py b/bindings/distrdf/test/backend/test_common.py index 9d9b8cb9912fc..b54afc7eeeec7 100644 --- a/bindings/distrdf/test/backend/test_common.py +++ b/bindings/distrdf/test/backend/test_common.py @@ -70,7 +70,7 @@ def test_initialization_runs_in_current_environment(self): """ def defineIntVariable(name, value): import ROOT - ROOT.gInterpreter.ProcessLine("int %s = %s;" % (name, value)) + ROOT.gCling.ProcessLine("int %s = %s;" % (name, value)) varvalue = 2 DistRDF.initialize(defineIntVariable, "myInt", varvalue) diff --git a/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/API.h b/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/API.h index 0a4e4b3f6ab31..3a3de474231f1 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/API.h +++ b/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/API.h @@ -25,15 +25,15 @@ #endif #include "Python.h" -#define CPYCPPYY_VERSION_HEX 0x010c10 +#define CPYCPPYY_VERSION_HEX 0x011200 // Cppyy types namespace Cppyy { - typedef size_t TCppScope_t; + typedef void* TCppScope_t; typedef TCppScope_t TCppType_t; typedef void* TCppEnum_t; typedef void* TCppObject_t; - typedef intptr_t TCppMethod_t; + typedef void* TCppMethod_t; typedef size_t TCppIndex_t; typedef void* TCppFuncAddr_t; @@ -124,6 +124,7 @@ class CPYCPPYY_CLASS_EXTERN Converter { // create a converter based on its full type name and dimensions CPYCPPYY_EXTERN Converter* CreateConverter(const std::string& name, cdims_t = 0); +CPYCPPYY_EXTERN Converter* CreateConverter(Cppyy::TCppType_t type, cdims_t = 0); // delete a previously created converter CPYCPPYY_EXTERN void DestroyConverter(Converter* p); @@ -154,6 +155,7 @@ class CPYCPPYY_CLASS_EXTERN Executor { // create an executor based on its full type name CPYCPPYY_EXTERN Executor* CreateExecutor(const std::string& name, cdims_t = 0); +CPYCPPYY_EXTERN Executor* CreateExecutor(Cppyy::TCppType_t type, cdims_t = 0); // delete a previously created executor CPYCPPYY_EXTERN void DestroyConverter(Converter* p); @@ -180,7 +182,8 @@ CPYCPPYY_EXTERN void* Instance_AsVoidPtr(PyObject* pyobject); // void* to C++ Instance (python object proxy) conversion, returns a new reference CPYCPPYY_EXTERN PyObject* Instance_FromVoidPtr( void* addr, const std::string& classname, bool python_owns = false); - +CPYCPPYY_EXTERN PyObject* Instance_FromVoidPtr( + void* addr, Cppyy::TCppScope_t klass_scope, bool python_owns = false); // type verifiers for C++ Scope CPYCPPYY_EXTERN bool Scope_Check(PyObject* pyobject); CPYCPPYY_EXTERN bool Scope_CheckExact(PyObject* pyobject); diff --git a/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/CommonDefs.h b/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/CommonDefs.h index e309c6a0f9b3b..af2fa60b71bf4 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/CommonDefs.h +++ b/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/CommonDefs.h @@ -6,6 +6,7 @@ #ifdef _MSC_VER // Windows requires symbols to be explicitly exported #define CPYCPPYY_EXPORT extern __declspec(dllexport) +#define CPYCPPYY_IMPORT extern __declspec(dllimport) #define CPYCPPYY_CLASS_EXPORT __declspec(dllexport) // CPYCPPYY_EXTERN is dual use in the public API @@ -13,8 +14,8 @@ #define CPYCPPYY_EXTERN extern __declspec(dllexport) #define CPYCPPYY_CLASS_EXTERN __declspec(dllexport) #else -#define CPYCPPYY_EXTERN extern -#define CPYCPPYY_CLASS_EXTERN +#define CPYCPPYY_EXTERN extern __declspec(dllimport) +#define CPYCPPYY_CLASS_EXTERN __declspec(dllimport) #endif #define CPYCPPYY_STATIC @@ -22,6 +23,7 @@ #else // Linux, Mac, etc. #define CPYCPPYY_EXPORT extern +#define CPYCPPYY_IMPORT extern #define CPYCPPYY_CLASS_EXPORT #define CPYCPPYY_EXTERN extern #define CPYCPPYY_CLASS_EXTERN @@ -29,6 +31,4 @@ #endif -#define CPYCPPYY_IMPORT extern - #endif // !CPYCPPYY_COMMONDEFS_H diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/API.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/API.cxx index ad57cd9c559e5..28ad7bb58b8f0 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/API.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/API.cxx @@ -135,6 +135,23 @@ PyObject* CPyCppyy::Instance_FromVoidPtr( return pyobject; } +//----------------------------------------------------------------------------- +PyObject* CPyCppyy::Instance_FromVoidPtr( + void* addr, Cppyy::TCppScope_t klass_scope, bool python_owns) +{ +// Bind the addr to a python object of class defined by classname. + if (!Initialize()) + return nullptr; + +// perform cast (the call will check TClass and addr, and set python errors) + PyObject* pyobject = BindCppObjectNoCast(addr, klass_scope, false); + +// give ownership, for ref-counting, to the python side, if so requested + if (python_owns && CPPInstance_Check(pyobject)) + ((CPPInstance*)pyobject)->PythonOwns(); + + return pyobject; +} namespace CPyCppyy { // version with C type arguments only for use with Numba PyObject* Instance_FromVoidPtr(void* addr, const char* classname, int python_owns) { @@ -226,7 +243,7 @@ bool CPyCppyy::Instance_IsLively(PyObject* pyobject) // the instance fails the lively test if it owns the C++ object while having a // reference count of 1 (meaning: it could delete the C++ instance any moment) - if (Py_REFCNT(pyobject) <= 1 && (((CPPInstance*)pyobject)->fFlags & CPPInstance::kIsOwner)) + if (pyobject->ob_refcnt <= 1 && (((CPPInstance*)pyobject)->fFlags & CPPInstance::kIsOwner)) return false; return true; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPConstructor.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPConstructor.cxx index f935d3d5371cb..d121364d839ba 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPConstructor.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPConstructor.cxx @@ -15,6 +15,8 @@ //- data _____________________________________________________________________ namespace CPyCppyy { extern PyObject* gNullPtrObject; + void* Instance_AsVoidPtr(PyObject* pyobject); + PyObject* Instance_FromVoidPtr(void* addr, Cppyy::TCppScope_t klass_scope, bool python_owns); } @@ -44,7 +46,7 @@ PyObject* CPyCppyy::CPPConstructor::Reflex( if (request == Cppyy::Reflex::RETURN_TYPE) { std::string fn = Cppyy::GetScopedFinalName(this->GetScope()); if (format == Cppyy::Reflex::OPTIMAL || format == Cppyy::Reflex::AS_TYPE) - return CreateScopeProxy(fn); + return CreateScopeProxy(this->GetScope()); else if (format == Cppyy::Reflex::AS_STRING) return CPyCppyy_PyText_FromString(fn.c_str()); } @@ -56,7 +58,6 @@ PyObject* CPyCppyy::CPPConstructor::Reflex( PyObject* CPyCppyy::CPPConstructor::Call(CPPInstance*& self, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject* kwds, CallContext* ctxt) { - // setup as necessary if (fArgsRequired == -1 && !this->Initialize(ctxt)) return nullptr; // important: 0, not Py_None @@ -78,15 +79,6 @@ PyObject* CPyCppyy::CPPConstructor::Call(CPPInstance*& self, return nullptr; } - const auto cppScopeFlags = ((CPPScope*)Py_TYPE(self))->fFlags; - -// Do nothing if the constructor is explicit and we are in an implicit -// conversion context. We recognize this by checking the CPPScope::kNoImplicit -// flag, as further implicit conversions are disabled to prevent infinite -// recursion. See also the ConvertImplicit() helper in Converters.cxx. - if((cppScopeFlags & CPPScope::kNoImplicit) && Cppyy::IsExplicit(GetMethod())) - return nullptr; - // self provides the python context for lifelines if (!ctxt->fPyContext) ctxt->fPyContext = (PyObject*)cargs.fSelf; // no Py_INCREF as no ownership @@ -135,7 +127,7 @@ PyObject* CPyCppyy::CPPConstructor::Call(CPPInstance*& self, } else { // translate the arguments - if (cppScopeFlags & CPPScope::kNoImplicit) + if (((CPPClass*)Py_TYPE(self))->fFlags & CPPScope::kNoImplicit) ctxt->fFlags |= CallContext::kNoImplicit; if (!this->ConvertAndSetArgs(cargs.fArgs, cargs.fNArgsf, ctxt)) return nullptr; @@ -182,6 +174,7 @@ PyObject* CPyCppyy::CPPConstructor::Call(CPPInstance*& self, return nullptr; } + //---------------------------------------------------------------------------- CPyCppyy::CPPMultiConstructor::CPPMultiConstructor(Cppyy::TCppScope_t scope, Cppyy::TCppMethod_t method) : CPPConstructor(scope, method) diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPDataMember.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPDataMember.cxx index 13b144b046045..109aa2be54f55 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPDataMember.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPDataMember.cxx @@ -4,6 +4,7 @@ #include "PyStrings.h" #include "CPPDataMember.h" #include "CPPInstance.h" +#include "CPPEnum.h" #include "Dimensions.h" #include "LowLevelViews.h" #include "ProxyWrappers.h" @@ -47,10 +48,6 @@ static PyObject* dm_get(CPPDataMember* dm, CPPInstance* pyobj, PyObject* /* kls } } -// non-initialized or public data accesses through class (e.g. by help()) - void* address = dm->GetAddress(pyobj); - if (!address || (intptr_t)address == -1 /* Cling error */) - return nullptr; if (dm->fFlags & (kIsEnumPrep | kIsEnumType)) { if (dm->fFlags & kIsEnumPrep) { @@ -58,19 +55,16 @@ static PyObject* dm_get(CPPDataMember* dm, CPPInstance* pyobj, PyObject* /* kls dm->fFlags &= ~kIsEnumPrep; // fDescription contains the full name of the actual enum value object - const std::string& lookup = CPyCppyy_PyText_AsString(dm->fDescription); - const std::string& enum_type = TypeManip::extract_namespace(lookup); - const std::string& enum_scope = TypeManip::extract_namespace(enum_type); + const Cppyy::TCppScope_t enum_type = Cppyy::GetParentScope(dm->fScope); + const Cppyy::TCppScope_t enum_scope = Cppyy::GetParentScope(enum_type); - PyObject* pyscope = nullptr; - if (enum_scope.empty()) pyscope = GetScopeProxy(Cppyy::gGlobalScope); - else pyscope = CreateScopeProxy(enum_scope); + PyObject* pyscope = CreateScopeProxy(enum_scope); if (pyscope) { - PyObject* pyEnumType = PyObject_GetAttrString(pyscope, - enum_type.substr(enum_scope.size() ? enum_scope.size()+2 : 0, std::string::npos).c_str()); + PyObject* pyEnumType = + PyObject_GetAttrString(pyscope, Cppyy::GetFinalName(enum_type).c_str()); if (pyEnumType) { - PyObject* pyval = PyObject_GetAttrString(pyEnumType, - lookup.substr(enum_type.size()+2, std::string::npos).c_str()); + PyObject* pyval = + PyObject_GetAttrString(pyEnumType, Cppyy::GetFinalName(dm->fScope).c_str()); Py_DECREF(pyEnumType); if (pyval) { Py_DECREF(dm->fDescription); @@ -88,7 +82,16 @@ static PyObject* dm_get(CPPDataMember* dm, CPPInstance* pyobj, PyObject* /* kls Py_INCREF(dm->fDescription); return dm->fDescription; } + + if (Cppyy::IsEnumConstant(dm->fScope)) { + // anonymous enum + return pyval_from_enum(Cppyy::ResolveEnum(dm->fScope), nullptr, nullptr, dm->fScope); + } } +// non-initialized or public data accesses through class (e.g. by help()) + void* address = dm->GetAddress(pyobj); + if (!address || (intptr_t)address == -1 /* Cling error */) + return nullptr; if (dm->fConverter != 0) { PyObject* result = dm->fConverter->FromMemory((dm->fFlags & kIsArrayType) ? &address : address); @@ -319,53 +322,54 @@ PyTypeObject CPPDataMember_Type = { //- public members ----------------------------------------------------------- -void CPyCppyy::CPPDataMember::Set(Cppyy::TCppScope_t scope, Cppyy::TCppIndex_t idata) +void CPyCppyy::CPPDataMember::Set(Cppyy::TCppScope_t scope, Cppyy::TCppScope_t data) { - fEnclosingScope = scope; - fOffset = Cppyy::GetDatamemberOffset(scope, idata); // TODO: make lazy - fFlags = Cppyy::IsStaticData(scope, idata) ? kIsStaticData : 0; - - std::vector dims; - int ndim = 0; Py_ssize_t size = 0; - while (0 < (size = Cppyy::GetDimensionSize(scope, idata, ndim))) { - ndim += 1; - if (size == INT_MAX) // meaning: incomplete array type - size = UNKNOWN_SIZE; - if (ndim == 1) dims.reserve(4); - dims.push_back((dim_t)size); + if (Cppyy::IsLambdaClass(Cppyy::GetDatamemberType(data))) { + fScope = Cppyy::WrapLambdaFromVariable(data); + } else { + fScope = data; } - if (!dims.empty()) - fFlags |= kIsArrayType; - const std::string name = Cppyy::GetDatamemberName(scope, idata); - fFullType = Cppyy::GetDatamemberType(scope, idata); - if (Cppyy::IsEnumData(scope, idata)) { + fEnclosingScope = scope; + fOffset = Cppyy::GetDatamemberOffset(fScope, fScope == data ? scope : Cppyy::GetScope("__cppyy_internal_wrap_g")); // XXX: Check back here // TODO: make lazy + fFlags = Cppyy::IsStaticDatamember(fScope) ? kIsStaticData : 0; + + const std::string name = Cppyy::GetFinalName(fScope); + Cppyy::TCppType_t type; + + + if (Cppyy::IsEnumConstant(fScope)) { + type = Cppyy::GetEnumConstantType(fScope); + fFullType = Cppyy::GetTypeAsString(type); if (fFullType.find("(anonymous)") == std::string::npos && fFullType.find("(unnamed)") == std::string::npos) { // repurpose fDescription for lazy lookup of the enum later fDescription = CPyCppyy_PyText_FromString((fFullType + "::" + name).c_str()); fFlags |= kIsEnumPrep; } - fFullType = Cppyy::ResolveEnum(fFullType); - fFlags |= kIsConstData; - } else if (Cppyy::IsConstData(scope, idata)) { + type = Cppyy::ResolveType(type); fFlags |= kIsConstData; + } else { + type = Cppyy::GetDatamemberType(fScope); + fFullType = Cppyy::GetTypeAsString(type); + + // Get the integer type if it's an enum + if (Cppyy::IsEnumType(type)) + type = Cppyy::ResolveType(type); + + if (Cppyy::IsConstVar(fScope)) + fFlags |= kIsConstData; } -// if this data member is an array, the conversion needs to be pointer to object for instances, -// to prevent the need for copying in the conversion; furthermore, fixed arrays' full type for -// builtins are not declared as such if more than 1-dim (TODO: fix in clingwrapper) - if (!dims.empty() && fFullType.back() != '*') { - if (Cppyy::GetScope(fFullType)) fFullType += '*'; - else if (fFullType.back() != ']') { - for (auto d: dims) fFullType += d == UNKNOWN_SIZE ? "*" : "[]"; - } - } + std::vector dims = Cppyy::GetDimensions(type); + + if (!dims.empty()) + fFlags |= kIsArrayType; if (dims.empty()) - fConverter = CreateConverter(fFullType); + fConverter = CreateConverter(type, 0); else - fConverter = CreateConverter(fFullType, {(dim_t)dims.size(), dims.data()}); + fConverter = CreateConverter(type, {(dim_t)dims.size(), dims.data()}); if (!(fFlags & kIsEnumPrep)) fDescription = CPyCppyy_PyText_FromString(name.c_str()); diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPDataMember.h b/bindings/pyroot/cppyy/CPyCppyy/src/CPPDataMember.h index 9fe32cfd93e72..9241b4dd267ef 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPDataMember.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPDataMember.h @@ -14,7 +14,7 @@ class CPPInstance; class CPPDataMember { public: - void Set(Cppyy::TCppScope_t scope, Cppyy::TCppIndex_t idata); + void Set(Cppyy::TCppScope_t scope, Cppyy::TCppScope_t var); void Set(Cppyy::TCppScope_t scope, const std::string& name, void* address); std::string GetName(); @@ -25,6 +25,7 @@ class CPPDataMember { intptr_t fOffset; long fFlags; Converter* fConverter; + Cppyy::TCppScope_t fScope; Cppyy::TCppScope_t fEnclosingScope; PyObject* fDescription; PyObject* fDoc; @@ -55,12 +56,12 @@ inline bool CPPDataMember_CheckExact(T* object) //- creation ----------------------------------------------------------------- inline CPPDataMember* CPPDataMember_New( - Cppyy::TCppScope_t scope, Cppyy::TCppIndex_t idata) + Cppyy::TCppScope_t scope, Cppyy::TCppScope_t var) { // Create an initialize a new property descriptor, given the C++ datum. CPPDataMember* pyprop = (CPPDataMember*)CPPDataMember_Type.tp_new(&CPPDataMember_Type, nullptr, nullptr); - pyprop->Set(scope, idata); + pyprop->Set(scope, var); return pyprop; } diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPEnum.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPEnum.cxx index dbb2f0fe4da29..6027db9776e35 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPEnum.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPEnum.cxx @@ -19,9 +19,9 @@ static PyObject* pytype_from_enum_type(const std::string& enum_type) } //---------------------------------------------------------------------------- -static PyObject* pyval_from_enum(const std::string& enum_type, PyObject* pytype, - PyObject* btype, Cppyy::TCppEnum_t etype, Cppyy::TCppIndex_t idata) { - long long llval = Cppyy::GetEnumDataValue(etype, idata); +PyObject* CPyCppyy::pyval_from_enum(const std::string& enum_type, PyObject* pytype, + PyObject* btype, Cppyy::TCppScope_t enum_constant) { + long long llval = Cppyy::GetEnumDataValue(enum_constant); if (enum_type == "bool") { PyObject* result = (bool)llval ? Py_True : Py_False; @@ -45,14 +45,15 @@ static PyObject* pyval_from_enum(const std::string& enum_type, PyObject* pytype, if (!bval) return nullptr; // e.g. when out of range for small integers - PyObject* args = PyTuple_New(1); - PyTuple_SET_ITEM(args, 0, bval); - PyObject* result = ((PyTypeObject*)btype)->tp_new((PyTypeObject*)pytype, args, nullptr); - Py_DECREF(args); - return result; + if (pytype && btype) { + PyObject* args = PyTuple_New(1); + PyTuple_SET_ITEM(args, 0, bval); + bval = ((PyTypeObject*)btype)->tp_new((PyTypeObject*)pytype, args, nullptr); + Py_DECREF(args); + } + return bval; } - //- enum methods ------------------------------------------------------------- static int enum_setattro(PyObject* /* pyclass */, PyObject* /* pyname */, PyObject* /* pyval */) { @@ -66,6 +67,8 @@ static PyObject* enum_repr(PyObject* self) { using namespace CPyCppyy; + PyObject* kls_scope = PyObject_GetAttr((PyObject*)Py_TYPE(self), PyStrings::gThisModule); + if (!kls_scope) PyErr_Clear(); PyObject* kls_cppname = PyObject_GetAttr((PyObject*)Py_TYPE(self), PyStrings::gCppName); if (!kls_cppname) PyErr_Clear(); PyObject* obj_cppname = PyObject_GetAttr(self, PyStrings::gCppName); @@ -74,7 +77,7 @@ static PyObject* enum_repr(PyObject* self) PyObject* repr = nullptr; if (kls_cppname && obj_cppname && obj_str) { - const std::string resolved = Cppyy::ResolveEnum(CPyCppyy_PyText_AsString(kls_cppname)); + const std::string resolved = Cppyy::ResolveEnum(PyLong_AsVoidPtr(kls_scope)); repr = CPyCppyy_PyText_FromFormat("(%s::%s) : (%s) %s", CPyCppyy_PyText_AsString(kls_cppname), CPyCppyy_PyText_AsString(obj_cppname), resolved.c_str(), CPyCppyy_PyText_AsString(obj_str)); @@ -144,12 +147,12 @@ CPyCppyy::CPPEnum* CPyCppyy::CPPEnum_New(const std::string& name, Cppyy::TCppSco CPPEnum* pyenum = nullptr; - const std::string& ename = scope == Cppyy::gGlobalScope ? name : Cppyy::GetScopedFinalName(scope)+"::"+name; - Cppyy::TCppEnum_t etype = Cppyy::GetEnum(scope, name); + Cppyy::TCppScope_t etype = scope; + const std::string& ename = Cppyy::GetScopedFinalName(scope); if (etype) { // create new enum type with labeled values in place, with a meta-class // to make sure the enum values are read-only - const std::string& resolved = Cppyy::ResolveEnum(ename); + const std::string& resolved = Cppyy::ResolveEnum(etype); PyObject* pyside_type = pytype_from_enum_type(resolved); PyObject* pymetabases = PyTuple_New(1); PyObject* btype = (PyObject*)Py_TYPE(pyside_type); @@ -169,12 +172,14 @@ CPyCppyy::CPPEnum* CPyCppyy::CPPEnum_New(const std::string& name, Cppyy::TCppSco // create the __cpp_name__ for templates PyObject* dct = PyDict_New(); PyObject* pycppname = CPyCppyy_PyText_FromString(ename.c_str()); + PyObject* pycppscope = PyLong_FromVoidPtr(etype); PyDict_SetItem(dct, PyStrings::gCppName, pycppname); + PyDict_SetItem(dct, PyStrings::gThisModule, pycppscope); Py_DECREF(pycppname); PyObject* pyresolved = CPyCppyy_PyText_FromString(resolved.c_str()); PyDict_SetItem(dct, PyStrings::gUnderlying, pyresolved); Py_DECREF(pyresolved); - + // add the __module__ to allow pickling std::string modname = TypeManip::extract_namespace(ename); TypeManip::cppscope_to_pyscope(modname); // :: -> . @@ -196,15 +201,15 @@ CPyCppyy::CPPEnum* CPyCppyy::CPPEnum_New(const std::string& name, Cppyy::TCppSco ((PyTypeObject*)pyenum)->tp_str = ((PyTypeObject*)pyside_type)->tp_repr; // collect the enum values - Cppyy::TCppIndex_t ndata = Cppyy::GetNumEnumData(etype); + std::vector econstants = Cppyy::GetEnumConstants(etype); bool values_ok = true; - for (Cppyy::TCppIndex_t idata = 0; idata < ndata; ++idata) { - PyObject* val = pyval_from_enum(resolved, pyenum, pyside_type, etype, idata); + for (auto *econstant : econstants) { + PyObject* val = pyval_from_enum(resolved, pyenum, pyside_type, econstant); if (!val) { values_ok = false; break; } - const std::string& dname = Cppyy::GetEnumDataName(etype, idata); + const std::string& dname = Cppyy::GetFinalName(econstant); PyObject* pydname = CPyCppyy_PyText_FromString(dname.c_str()); PyObject_SetAttr(pyenum, pydname, val); Py_DECREF(pydname); @@ -235,4 +240,4 @@ CPyCppyy::CPPEnum* CPyCppyy::CPPEnum_New(const std::string& name, Cppyy::TCppSco } return pyenum; -} +} \ No newline at end of file diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPEnum.h b/bindings/pyroot/cppyy/CPyCppyy/src/CPPEnum.h index 730baf4ba75d9..d1d0e9eb670bf 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPEnum.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPEnum.h @@ -11,6 +11,8 @@ typedef PyObject CPPEnum; //- creation ----------------------------------------------------------------- CPPEnum* CPPEnum_New(const std::string& name, Cppyy::TCppScope_t scope); +PyObject* pyval_from_enum(const std::string& enum_type, PyObject* pytype, + PyObject* btype, Cppyy::TCppScope_t enum_constant); } // namespace CPyCppyy #endif // !CPYCPPYY_CPPENUM_H diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.cxx index 360b45a9e51cd..bea04d2c1bdeb 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.cxx @@ -274,7 +274,7 @@ static PyObject* op_destruct(CPPInstance* self) } //= CPyCppyy object dispatch support ========================================= -static PyObject* op_dispatch(PyObject* self, PyObject* args, PyObject* /* kwds */) +static PyObject* op_dispatch(PyObject* self, PyObject* args, PyObject* /* kdws */) { // User-side __dispatch__ method to allow selection of a specific overloaded // method. The actual selection is in the __overload__() method of CPPOverload. @@ -431,7 +431,6 @@ static PyMethodDef op_methods[] = { {(char*)nullptr, nullptr, 0, nullptr} }; - //= CPyCppyy object proxy construction/destruction =========================== static CPPInstance* op_new(PyTypeObject* subtype, PyObject*, PyObject*) { @@ -488,6 +487,10 @@ static inline PyObject* eqneq_binop(CPPClass* klass, PyObject* self, PyObject* o bool flipit = false; PyObject* binop = op == Py_EQ ? klass->fOperators->fEq : klass->fOperators->fNe; + if (!binop) { + binop = op == Py_EQ ? klass->fOperators->fNe : klass->fOperators->fEq; + if (binop) flipit = true; + } if (!binop) { const char* cppop = op == Py_EQ ? "==" : "!="; PyCallable* pyfunc = FindBinaryOperator(self, obj, cppop); @@ -501,11 +504,6 @@ static inline PyObject* eqneq_binop(CPPClass* klass, PyObject* self, PyObject* o else klass->fOperators->fNe = binop; } - if (binop == Py_None) { // can try !== or !!= as alternatives - binop = op == Py_EQ ? klass->fOperators->fNe : klass->fOperators->fEq; - if (binop && binop != Py_None) flipit = true; - } - if (!binop || binop == Py_None) return nullptr; PyObject* args = PyTuple_New(1); @@ -537,8 +535,8 @@ static inline void* cast_actual(void* obj) { if (((CPPInstance*)obj)->fFlags & CPPInstance::kIsActual) return address; - Cppyy::TCppType_t klass = ((CPPClass*)Py_TYPE((PyObject*)obj))->fCppType; - Cppyy::TCppType_t clActual = Cppyy::GetActualClass(klass, address); + Cppyy::TCppScope_t klass = ((CPPClass*)Py_TYPE((PyObject*)obj))->fCppType; + Cppyy::TCppScope_t clActual = klass /* XXX: Cppyy::GetActualClass(klass, address) */; if (clActual && clActual != klass) { intptr_t offset = Cppyy::GetBaseOffset( clActual, klass, address, -1 /* down-cast */, true /* report errors */); @@ -694,7 +692,7 @@ static Py_hash_t op_hash(CPPInstance* self) return h; } - Cppyy::TCppScope_t stdhash = Cppyy::GetScope("std::hash<"+Cppyy::GetScopedFinalName(self->ObjectIsA())+">"); + Cppyy::TCppScope_t stdhash = Cppyy::GetFullScope("std::hash<"+Cppyy::GetScopedFinalName(self->ObjectIsA())+">"); if (stdhash) { PyObject* hashcls = CreateScopeProxy(stdhash); PyObject* dct = PyObject_GetAttr(hashcls, PyStrings::gDict); @@ -725,7 +723,7 @@ static Py_hash_t op_hash(CPPInstance* self) //---------------------------------------------------------------------------- static PyObject* op_str_internal(PyObject* pyobj, PyObject* lshift, bool isBound) { - static Cppyy::TCppScope_t sOStringStreamID = Cppyy::GetScope("std::ostringstream"); + static Cppyy::TCppScope_t sOStringStreamID = Cppyy::GetFullScope("std::ostringstream"); std::ostringstream s; PyObject* pys = BindCppObjectNoCast(&s, sOStringStreamID); Py_INCREF(pys); @@ -786,7 +784,7 @@ static PyObject* op_str(CPPInstance* self) // normal lookup failed; attempt lazy install of global operator<<(ostream&, type&) std::string rcname = Utility::ClassName((PyObject*)self); Cppyy::TCppScope_t rnsID = Cppyy::GetScope(TypeManip::extract_namespace(rcname)); - PyCallable* pyfunc = Utility::FindBinaryOperator("std::ostream", rcname, "<<", rnsID); + PyCallable* pyfunc = Utility::FindBinaryOperator("std::ostream&", rcname, "<<", rnsID); if (!pyfunc) continue; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.h b/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.h index fcb0ad3d5d11a..e17c324dc444e 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.h @@ -15,8 +15,8 @@ #include "CallContext.h" // for Parameter // Standard -#include #include +#include #include @@ -63,7 +63,7 @@ class CPPInstance { // access to C++ pointer and type void* GetObject(); void*& GetObjectRaw() { return IsExtended() ? *(void**) fObject : fObject; } - Cppyy::TCppType_t ObjectIsA(bool check_smart = true) const; + Cppyy::TCppScope_t ObjectIsA(bool check_smart = true) const; // memory management: ownership of the underlying C++ object void PythonOwns(); @@ -87,7 +87,8 @@ class CPPInstance { // implementation of the __reduce__ method: doesn't wrap any function by // default but can be re-assigned by libraries that add C++ object // serialization support, like ROOT - static std::function &ReduceMethod(); +static std::function &ReduceMethod(); + private: void CreateExtension(); @@ -117,7 +118,7 @@ inline void* CPPInstance::GetObject() } //---------------------------------------------------------------------------- -inline Cppyy::TCppType_t CPPInstance::ObjectIsA(bool check_smart) const +inline Cppyy::TCppScope_t CPPInstance::ObjectIsA(bool check_smart) const { // Retrieve the C++ type identifier (or raw type if smart). if (check_smart || !IsSmart()) return ((CPPClass*)Py_TYPE(this))->fCppType; @@ -126,12 +127,7 @@ inline Cppyy::TCppType_t CPPInstance::ObjectIsA(bool check_smart) const //- object proxy type and type verification ---------------------------------- -// Needs to be extern because the libROOTPythonizations is secretly using it -#ifdef _MSC_VER -extern __declspec(dllimport) PyTypeObject CPPInstance_Type; -#else -extern PyTypeObject CPPInstance_Type; -#endif +CPYCPPYY_IMPORT PyTypeObject CPPInstance_Type; template inline bool CPPInstance_Check(T* object) diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx index 1900590b90602..fbe7148a9d145 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx @@ -126,7 +126,7 @@ inline PyObject* CPyCppyy::CPPMethod::ExecuteFast( result = nullptr; // error already set } catch (std::exception& e) { // attempt to set the exception to the actual type, to allow catching with the Python C++ type - static Cppyy::TCppType_t exc_type = (Cppyy::TCppType_t)Cppyy::GetScope("std::exception"); + static Cppyy::TCppType_t exc_type = (Cppyy::TCppType_t)Cppyy::GetFullScope("std::exception"); ctxt->fFlags |= CallContext::kCppException; @@ -237,10 +237,11 @@ bool CPyCppyy::CPPMethod::InitConverters_() // setup the dispatch cache for (int iarg = 0; iarg < (int)nArgs; ++iarg) { - const std::string& fullType = Cppyy::GetMethodArgType(fMethod, iarg); + Cppyy::TCppType_t fullType = Cppyy::GetMethodArgType(fMethod, iarg); Converter* conv = CreateConverter(fullType); if (!conv) { - PyErr_Format(PyExc_TypeError, "argument type %s not handled", fullType.c_str()); + PyErr_Format(PyExc_TypeError, "argument type %s not handled", + Cppyy::GetTypeAsString(fullType).c_str()); return false; } @@ -254,9 +255,9 @@ bool CPyCppyy::CPPMethod::InitConverters_() bool CPyCppyy::CPPMethod::InitExecutor_(Executor*& executor, CallContext* /* ctxt */) { // install executor conform to the return type - executor = CreateExecutor( - (bool)fMethod == true ? Cppyy::GetMethodResultType(fMethod) \ - : Cppyy::GetScopedFinalName(fScope)); + executor = + (bool)fMethod == true ? CreateExecutor(Cppyy::GetMethodReturnType(fMethod)) \ + : CreateExecutor(Cppyy::GetScopedFinalName(fScope)); if (!executor) return false; @@ -274,83 +275,77 @@ std::string CPyCppyy::CPPMethod::GetSignatureString(bool fa) //---------------------------------------------------------------------------- void CPyCppyy::CPPMethod::SetPyError_(PyObject* msg) { -// Helper to report errors in a consistent format (derefs msg). -// -// Handles three cases: -// 1. No Python error occured yet: -// Set a new TypeError with the message "msg" and the docstring of this -// C++ method to give some context. -// 2. A C++ exception has occured: -// Augment the exception message with the docstring of this method -// 3. A Python exception has occured: -// Do nothing, Python exceptions are already informative enough - -#if PY_VERSION_HEX >= 0x030c0000 - PyObject *evalue = PyErr_Occurred() ? PyErr_GetRaisedException() : nullptr; - PyObject *etype = evalue ? (PyObject *)Py_TYPE(evalue) : nullptr; -#else - PyObject *etype = nullptr; - PyObject *evalue = nullptr; - PyObject *etrace = nullptr; +// helper to report errors in a consistent format (derefs msg) + std::string details{}; + PyObject *etype = nullptr, *evalue = nullptr; if (PyErr_Occurred()) { + PyObject* etrace = nullptr; + PyErr_Fetch(&etype, &evalue, &etrace); - } -#endif - const bool isCppExc = evalue && PyType_IsSubtype((PyTypeObject*)etype, &CPPExcInstance_Type); - // If the error is not a CPPExcInstance, the error from Python itself is - // already complete and messing with it would only make it less informative. - // Just restore and return. - if (evalue && !isCppExc) { -#if PY_VERSION_HEX >= 0x030c0000 - PyErr_SetRaisedException(evalue); -#else - PyErr_Restore(etype, evalue, etrace); -#endif - return; - } + if (evalue) { + PyObject* descr = PyObject_Str(evalue); + if (descr) { + details = CPyCppyy_PyText_AsString(descr); + Py_DECREF(descr); + } + } + + Py_XDECREF(etrace); + } PyObject* doc = GetDocString(); - const char* cdoc = CPyCppyy_PyText_AsString(doc); - const char* cmsg = msg ? CPyCppyy_PyText_AsString(msg) : nullptr; - PyObject* errtype = etype ? etype : PyExc_TypeError; + PyObject* errtype = etype; + if (!errtype) + errtype = PyExc_TypeError; PyObject* pyname = PyObject_GetAttr(errtype, PyStrings::gName); const char* cname = pyname ? CPyCppyy_PyText_AsString(pyname) : "Exception"; - if (!isCppExc) { - // this is the case where no Python error has occured yet, and we set a new - // error with context info - PyErr_Format(errtype, "%s =>\n %s: %s", cdoc, cname, cmsg ? cmsg : ""); + if (!PyType_IsSubtype((PyTypeObject*)errtype, &CPPExcInstance_Type)) { + if (details.empty()) { + PyErr_Format(errtype, "%s =>\n %s: %s", CPyCppyy_PyText_AsString(doc), + cname, msg ? CPyCppyy_PyText_AsString(msg) : ""); + } else if (msg) { + PyErr_Format(errtype, "%s =>\n %s: %s (%s)", + CPyCppyy_PyText_AsString(doc), cname, CPyCppyy_PyText_AsString(msg), + details.c_str()); + } else { + PyErr_Format(errtype, "%s =>\n %s: %s", + CPyCppyy_PyText_AsString(doc), cname, details.c_str()); + } } else { - // augment the top message with context information - PyObject *&topMessage = ((CPPExcInstance*)evalue)->fTopMessage; - Py_XDECREF(topMessage); + Py_XDECREF(((CPPExcInstance*)evalue)->fTopMessage); if (msg) { - topMessage = CPyCppyy_PyText_FromFormat("%s =>\n %s: %s | ", cdoc, cname, cmsg); + ((CPPExcInstance*)evalue)->fTopMessage = CPyCppyy_PyText_FromFormat(\ + "%s =>\n %s: %s | ", CPyCppyy_PyText_AsString(doc), cname, CPyCppyy_PyText_AsString(msg)); } else { - topMessage = CPyCppyy_PyText_FromFormat("%s =>\n %s: ", cdoc, cname); + ((CPPExcInstance*)evalue)->fTopMessage = CPyCppyy_PyText_FromFormat(\ + "%s =>\n %s: ", CPyCppyy_PyText_AsString(doc), cname); } - // restore the updated error -#if PY_VERSION_HEX >= 0x030c0000 - PyErr_SetRaisedException(evalue); -#else - PyErr_Restore(etype, evalue, etrace); -#endif + PyErr_SetObject(errtype, evalue); } Py_XDECREF(pyname); + Py_XDECREF(evalue); + Py_XDECREF(etype); Py_DECREF(doc); Py_XDECREF(msg); } +extern std::map TypeReductionMap; + //- constructors and destructor ---------------------------------------------- CPyCppyy::CPPMethod::CPPMethod( Cppyy::TCppScope_t scope, Cppyy::TCppMethod_t method) : fMethod(method), fScope(scope), fExecutor(nullptr), fArgIndices(nullptr), fArgsRequired(-1) { - // empty + Cppyy::TCppType_t result = Cppyy::ResolveType(Cppyy::GetMethodReturnType(fMethod)); + if (TypeReductionMap.contains(result)) + fMethod = Cppyy::ReduceReturnType(fMethod, TypeReductionMap[result]); + if (result && Cppyy::IsLambdaClass(result)) + fMethod = Cppyy::AdaptFunctionForLambdaReturn(fMethod); } //---------------------------------------------------------------------------- @@ -396,7 +391,7 @@ CPyCppyy::CPPMethod::~CPPMethod() * namespace c { * int foo(int x); * }}} - * + * * This function returns: * * 'int foo(int x)' @@ -410,12 +405,10 @@ PyObject* CPyCppyy::CPPMethod::GetPrototype(bool fa) // gives // a::b std::string finalscope = Cppyy::GetScopedFinalName(fScope); - return CPyCppyy_PyText_FromFormat("%s%s %s%s%s%s", + return CPyCppyy_PyText_FromFormat("%s%s %s%s", (Cppyy::IsStaticMethod(fMethod) ? "static " : ""), - Cppyy::GetMethodResultType(fMethod).c_str(), - finalscope.c_str(), - (finalscope.empty() ? "" : "::"), // Add final set of '::' if the method is scoped in namespace(s) - Cppyy::GetMethodName(fMethod).c_str(), + Cppyy::GetMethodReturnTypeAsString(fMethod).c_str(), + Cppyy::GetScopedFinalName(fMethod).c_str(), GetSignatureString(fa).c_str()); } @@ -479,7 +472,10 @@ int CPyCppyy::CPPMethod::GetPriority() const size_t nArgs = Cppyy::GetMethodNumArgs(fMethod); for (int iarg = 0; iarg < (int)nArgs; ++iarg) { - const std::string aname = Cppyy::GetMethodArgType(fMethod, iarg); + const std::string aname = Cppyy::GetMethodArgTypeAsString(fMethod, iarg); + // FIXME: convert the string comparisons with comparison to the underlying + // type: + // Cppyy::TCppType_t type = Cppyy::GetMethodArgType(fMethod, iarg); if (Cppyy::IsBuiltin(aname)) { // complex type (note: double penalty: for complex and the template type) @@ -528,7 +524,7 @@ int CPyCppyy::CPPMethod::GetPriority() if (scope) priority += static_cast(Cppyy::GetNumBasesLongestBranch(scope)); - if (Cppyy::IsEnum(clean_name)) + if (Cppyy::IsEnumScope(scope)) priority -= 100; // a couple of special cases as explained above @@ -536,7 +532,7 @@ int CPyCppyy::CPPMethod::GetPriority() priority += 150; // needed for proper implicit conversion rules } else if (aname.rfind("&&", aname.size()-2) != std::string::npos) { priority += 100; // prefer moves over other ref/ptr - } else if (scope && !Cppyy::IsComplete(clean_name)) { + } else if (scope && !Cppyy::IsComplete(scope)) { // class is known, but no dictionary available, 2 more cases: * and & if (aname[aname.size() - 1] == '&') priority += -5000; @@ -554,6 +550,10 @@ int CPyCppyy::CPPMethod::GetPriority() if (Cppyy::IsConstMethod(fMethod) && Cppyy::GetMethodName(fMethod) == "operator[]") priority += -10; + // constructors are prefered + if (Cppyy::IsConstructor(fMethod)) + priority += 100; + return priority; } @@ -568,8 +568,8 @@ bool CPyCppyy::CPPMethod::IsGreedy() if (!nArgs) return false; for (int iarg = 0; iarg < (int)nArgs; ++iarg) { - const std::string aname = Cppyy::GetMethodArgType(fMethod, iarg); - if (aname.find("void*") != 0) + const std::string aname = Cppyy::GetMethodArgTypeAsString(fMethod, iarg); + if (aname.find("void *") != 0) return false; } return true; @@ -593,7 +593,7 @@ PyObject* CPyCppyy::CPPMethod::GetCoVarNames() PyObject* co_varnames = PyTuple_New(co_argcount+1 /* self */); PyTuple_SET_ITEM(co_varnames, 0, CPyCppyy_PyText_FromString("self")); for (int iarg = 0; iarg < co_argcount; ++iarg) { - std::string argrep = Cppyy::GetMethodArgType(fMethod, iarg); + std::string argrep = Cppyy::GetMethodArgTypeAsString(fMethod, iarg); const std::string& parname = Cppyy::GetMethodArgName(fMethod, iarg); if (!parname.empty()) { argrep += " "; @@ -910,10 +910,10 @@ bool CPyCppyy::CPPMethod::ProcessArgs(PyCallArgs& cargs) // demand CPyCppyy object, and an argument that may match down the road if (CPPInstance_Check(pyobj)) { Cppyy::TCppType_t oisa = pyobj->ObjectIsA(); - if (fScope == Cppyy::gGlobalScope || // free global + if (fScope == Cppyy::GetGlobalScope() || // free global oisa == 0 || // null pointer or ctor call oisa == fScope || // matching types - Cppyy::IsSubtype(oisa, fScope)) { // id. + Cppyy::IsSubclass(oisa, fScope)) { // id. // reset self Py_INCREF(pyobj); // corresponding Py_DECREF is in CPPOverload @@ -965,7 +965,7 @@ bool CPyCppyy::CPPMethod::ConvertAndSetArgs(CPyCppyy_PyArgs_t args, size_t nargs Parameter* cppArgs = ctxt->GetArgs(argc); for (int i = 0; i < (int)argc; ++i) { if (!fConverters[i]->SetArg(CPyCppyy_PyArgs_GET_ITEM(args, i), cppArgs[i], ctxt)) { - SetPyError_(CPyCppyy_PyText_FromFormat("could not convert argument %d", i+1)); + SetPyError_(CPyCppyy_PyText_FromFormat("could not convert argument %d: %s", i+1, fConverters[i]->GetFailureMsg().c_str())); isOK = false; break; } @@ -980,7 +980,8 @@ PyObject* CPyCppyy::CPPMethod::Execute(void* self, ptrdiff_t offset, CallContext // call the interface method PyObject* result = 0; - if (!(CallContext::GlobalPolicyFlags() & CallContext::kProtected) && !(ctxt->fFlags & CallContext::kProtected)) { + if (CallContext::sSignalPolicy != CallContext::kProtected && \ + !(ctxt->fFlags & CallContext::kProtected)) { // bypasses try block (i.e. segfaults will abort) result = ExecuteFast(self, offset, ctxt); } else { @@ -1054,72 +1055,8 @@ PyObject* CPyCppyy::CPPMethod::GetSignature(bool fa) return CPyCppyy_PyText_FromString(GetSignatureString(fa).c_str()); } -/** - * @brief Returns a tuple with the names of the input parameters of this method. - * - * For example given a function with prototype: - * - * double foo(int a, float b, double c) - * - * this function returns: - * - * ('a', 'b', 'c') - */ -PyObject *CPyCppyy::CPPMethod::GetSignatureNames() -{ - // Build a tuple of the argument names for this signature. - int argcount = GetMaxArgs(); - PyObject *signature_names = PyTuple_New(argcount); - - for (int iarg = 0; iarg < argcount; ++iarg) { - const std::string &argname_cpp = Cppyy::GetMethodArgName(fMethod, iarg); - PyObject *argname_py = CPyCppyy_PyText_FromString(argname_cpp.c_str()); - PyTuple_SET_ITEM(signature_names, iarg, argname_py); - } - - return signature_names; -} - -/** - * @brief Returns a dictionary with the types of the signature of this method. - * - * This dictionary will store both the return type and the input parameter - * types of this method, respectively with keys "return_type" and - * "input_types", for example given a function with prototype: - * - * double foo(int a, float b, double c) - * - * this function returns: - * - * {'input_types': ('int', 'float', 'double'), 'return_type': 'double'} - */ -PyObject *CPyCppyy::CPPMethod::GetSignatureTypes() -{ - - PyObject *signature_types_dict = PyDict_New(); - - // Insert the return type first - std::string return_type = GetReturnTypeName(); - PyObject *return_type_py = CPyCppyy_PyText_FromString(return_type.c_str()); - PyDict_SetItem(signature_types_dict, CPyCppyy_PyText_FromString("return_type"), return_type_py); - - // Build a tuple of the argument types for this signature. - int argcount = GetMaxArgs(); - PyObject *parameter_types = PyTuple_New(argcount); - - for (int iarg = 0; iarg < argcount; ++iarg) { - const std::string &argtype_cpp = Cppyy::GetMethodArgType(fMethod, iarg); - PyObject *argtype_py = CPyCppyy_PyText_FromString(argtype_cpp.c_str()); - PyTuple_SET_ITEM(parameter_types, iarg, argtype_py); - } - - PyDict_SetItem(signature_types_dict, CPyCppyy_PyText_FromString("input_types"), parameter_types); - - return signature_types_dict; -} - //---------------------------------------------------------------------------- std::string CPyCppyy::CPPMethod::GetReturnTypeName() { - return Cppyy::GetMethodResultType(fMethod); + return Cppyy::GetMethodReturnTypeAsString(fMethod); } diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.h b/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.h index e9558f5c6869b..88e49621ef629 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPMethod.h @@ -51,8 +51,6 @@ class CPPMethod : public PyCallable { public: PyObject* GetSignature(bool show_formalargs = true) override; - PyObject* GetSignatureNames() override; - PyObject* GetSignatureTypes() override; PyObject* GetPrototype(bool show_formalargs = true) override; PyObject* GetTypeName() override; PyObject* Reflex(Cppyy::Reflex::RequestId_t request, diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPOperator.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPOperator.cxx index 4832136d74cd2..9aacc3dd50528 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPOperator.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPOperator.cxx @@ -2,7 +2,6 @@ #include "CPyCppyy.h" #include "CPPOperator.h" #include "CPPInstance.h" -#include "Utility.h" //- constructor -------------------------------------------------------------- @@ -50,14 +49,17 @@ PyObject* CPyCppyy::CPPOperator::Call(CPPInstance*& self, return result; } -// fetch the current error, resetting the error buffer - auto error = CPyCppyy::Utility::FetchPyError(); + PyObject* pytype = 0, *pyvalue = 0, *pytrace = 0; + PyErr_Fetch(&pytype, &pyvalue, &pytrace); result = fStub((PyObject*)self, CPyCppyy_PyArgs_GET_ITEM(args, idx_other)); -// if there was still a problem, restore the Python error buffer - if (!result) { - CPyCppyy::Utility::RestorePyError(error); + if (!result) + PyErr_Restore(pytype, pyvalue, pytrace); + else { + Py_XDECREF(pytype); + Py_XDECREF(pyvalue); + Py_XDECREF(pytrace); } return result; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx index f8eabb993c406..92e87a3bc3d98 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx @@ -62,12 +62,6 @@ class TPythonCallback : public PyCallable { PyObject* GetSignature(bool /*show_formalargs*/ = true) override { return CPyCppyy_PyText_FromString("*args, **kwargs"); } - PyObject* GetSignatureNames() override { - return PyTuple_New(0); - } - PyObject* GetSignatureTypes() override { - return PyTuple_New(0); - } PyObject* GetPrototype(bool /*show_formalargs*/ = true) override { return CPyCppyy_PyText_FromString(""); } @@ -291,54 +285,6 @@ static int mp_doc_set(CPPOverload* pymeth, PyObject *val, void *) return 0; } -/** - * @brief Returns a dictionary with the input parameter names for all overloads. - * - * This dictionary may look like: - * - * {'double ::foo(int a, float b, double c)': ('a', 'b', 'c'), - * 'float ::foo(float b)': ('b',), - * 'int ::foo(int a)': ('a',), - * 'int ::foo(int a, float b)': ('a', 'b')} - */ -static PyObject *mp_func_overloads_names(CPPOverload *pymeth) -{ - - const CPPOverload::Methods_t &methods = pymeth->fMethodInfo->fMethods; - - PyObject *overloads_names_dict = PyDict_New(); - - for (PyCallable *method : methods) { - PyDict_SetItem(overloads_names_dict, method->GetPrototype(), method->GetSignatureNames()); - } - - return overloads_names_dict; -} - -/** - * @brief Returns a dictionary with the types of all overloads. - * - * This dictionary may look like: - * - * {'double ::foo(int a, float b, double c)': {'input_types': ('int', 'float', 'double'), 'return_type': 'double'}, - * 'float ::foo(float b)': {'input_types': ('float',), 'return_type': 'float'}, - * 'int ::foo(int a)': {'input_types': ('int',), 'return_type': 'int'}, - * 'int ::foo(int a, float b)': {'input_types': ('int', 'float'), 'return_type': 'int'}} - */ -static PyObject *mp_func_overloads_types(CPPOverload *pymeth) -{ - - const CPPOverload::Methods_t &methods = pymeth->fMethodInfo->fMethods; - - PyObject *overloads_types_dict = PyDict_New(); - - for (PyCallable *method : methods) { - PyDict_SetItem(overloads_types_dict, method->GetPrototype(), method->GetSignatureTypes()); - } - - return overloads_types_dict; -} - //---------------------------------------------------------------------------- static PyObject* mp_meth_func(CPPOverload* pymeth, void*) { @@ -549,25 +495,40 @@ static int mp_setcreates(CPPOverload* pymeth, PyObject* value, void*) return set_flag(pymeth, value, CallContext::kIsCreator, "__creates__"); } -constexpr const char *mempolicy_error_message = - "The __mempolicy__ attribute can't be used, because in the past it was reserved to manage the local memory policy. " - "If you want to do that now, please implement a pythonization for your class that uses SetOwnership() to manage the " - "ownership of arguments according to your needs."; - //---------------------------------------------------------------------------- -static PyObject* mp_getmempolicy(CPPOverload*, void*) +static PyObject* mp_getmempolicy(CPPOverload* pymeth, void*) { - PyErr_SetString(PyExc_RuntimeError, mempolicy_error_message); - return nullptr; +// Get '_mempolicy' enum, which determines ownership of call arguments. + if (pymeth->fMethodInfo->fFlags & CallContext::kUseHeuristics) + return PyInt_FromLong(CallContext::kUseHeuristics); + + if (pymeth->fMethodInfo->fFlags & CallContext::kUseStrict) + return PyInt_FromLong(CallContext::kUseStrict); + + return PyInt_FromLong(-1); } //---------------------------------------------------------------------------- -static int mp_setmempolicy(CPPOverload*, PyObject*, void*) +static int mp_setmempolicy(CPPOverload* pymeth, PyObject* value, void*) { - PyErr_SetString(PyExc_RuntimeError, mempolicy_error_message); - return -1; +// Set '_mempolicy' enum, which determines ownership of call arguments. + long mempolicy = PyLong_AsLong(value); + if (mempolicy == CallContext::kUseHeuristics) { + pymeth->fMethodInfo->fFlags |= CallContext::kUseHeuristics; + pymeth->fMethodInfo->fFlags &= ~CallContext::kUseStrict; + } else if (mempolicy == CallContext::kUseStrict) { + pymeth->fMethodInfo->fFlags |= CallContext::kUseStrict; + pymeth->fMethodInfo->fFlags &= ~CallContext::kUseHeuristics; + } else { + PyErr_SetString(PyExc_ValueError, + "expected kMemoryStrict or kMemoryHeuristics as value for __mempolicy__"); + return -1; + } + + return 0; } + //---------------------------------------------------------------------------- #define CPPYY_BOOLEAN_PROPERTY(name, flag, label) \ static PyObject* mp_get##name(CPPOverload* pymeth, void*) { \ @@ -621,15 +582,13 @@ static PyGetSetDef mp_getset[] = { {(char*)"func_globals", (getter)mp_func_globals, nullptr, nullptr, nullptr}, {(char*)"func_doc", (getter)mp_doc, (setter)mp_doc_set, nullptr, nullptr}, {(char*)"func_name", (getter)mp_name, nullptr, nullptr, nullptr}, - {(char*)"func_overloads_types", (getter)mp_func_overloads_types, nullptr, nullptr, nullptr}, - {(char*)"func_overloads_names", (getter)mp_func_overloads_names, nullptr, nullptr, nullptr}, // flags to control behavior {(char*)"__creates__", (getter)mp_getcreates, (setter)mp_setcreates, (char*)"For ownership rules of result: if true, objects are python-owned", nullptr}, - {(char*)"__mempolicy__", (getter)mp_getmempolicy, (setter)mp_setmempolicy, - (char*)"Unused", nullptr}, + {(char*)"__mempolicy__", (getter)mp_getmempolicy, (setter)mp_setmempolicy, + (char*)"For argument ownership rules: like global, either heuristic or strict", nullptr}, {(char*)"__set_lifeline__", (getter)mp_getlifeline, (setter)mp_setlifeline, (char*)"If true, set a lifeline from the return value onto self", nullptr}, {(char*)"__release_gil__", (getter)mp_getthreaded, (setter)mp_setthreaded, @@ -671,6 +630,8 @@ static PyObject* mp_call(CPPOverload* pymeth, PyObject* args, PyObject* kwds) CallContext ctxt{}; const auto mflags = pymeth->fMethodInfo->fFlags; + const auto mempolicy = (mflags & (CallContext::kUseHeuristics | CallContext::kUseStrict)); + ctxt.fFlags |= mempolicy ? mempolicy : (uint64_t)CallContext::sMemoryPolicy; ctxt.fFlags |= (mflags & CallContext::kReleaseGIL); ctxt.fFlags |= (mflags & CallContext::kProtected); if (IsConstructor(pymeth->fMethodInfo->fFlags)) ctxt.fFlags |= CallContext::kIsConstructor; @@ -751,6 +712,9 @@ static PyObject* mp_call(CPPOverload* pymeth, PyObject* args, PyObject* kwds) } } + // clear collected errors + if (!errors.empty()) + std::for_each(errors.begin(), errors.end(), Utility::PyError_t::Clear); return HandleReturn(pymeth, im_self, result); } @@ -794,7 +758,7 @@ static PyObject* mp_call(CPPOverload* pymeth, PyObject* args, PyObject* kwds) // first summarize, then add details PyObject* topmsg = CPyCppyy_PyText_FromFormat( "none of the %d overloaded methods succeeded. Full details:", (int)nMethods); - SetDetailedException(std::move(errors), topmsg /* steals */, PyExc_TypeError /* default error */); + SetDetailedException(errors, topmsg /* steals */, PyExc_TypeError /* default error */); // report failure return nullptr; @@ -1115,19 +1079,10 @@ void CPyCppyy::CPPOverload::Set(const std::string& name, std::vectorfFlags |= (CallContext::kIsCreator | CallContext::kIsConstructor); -// special case, in heuristics mode also tag *Clone* methods as creators. Only -// check that Clone is present in the method name, not in the template argument -// list. - if (CallContext::GlobalPolicyFlags() & CallContext::kUseHeuristics) { - std::string_view name_maybe_template = name; - auto begin_template = name_maybe_template.find_first_of('<'); - if (begin_template <= name_maybe_template.size()) { - name_maybe_template = name_maybe_template.substr(0, begin_template); - } - if (name_maybe_template.find("Clone") != std::string_view::npos) { - fMethodInfo->fFlags |= CallContext::kIsCreator; - } - } +// special case, in heuristics mode also tag *Clone* methods as creators + if (CallContext::sMemoryPolicy == CallContext::kUseHeuristics && \ + name.find("Clone") != std::string::npos) + fMethodInfo->fFlags |= CallContext::kIsCreator; #if PY_VERSION_HEX >= 0x03080000 fVectorCall = (vectorcallfunc)mp_vectorcall; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.h b/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.h index 8bd3b2de234a9..b392bf4ed7f20 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPOverload.h @@ -25,11 +25,7 @@ inline uint64_t HashSignature(CPyCppyy_PyArgs_t args, size_t nargsf) // improved overloads for implicit conversions PyObject* pyobj = CPyCppyy_PyArgs_GET_ITEM(args, i); hash += (uint64_t)Py_TYPE(pyobj); -#if PY_VERSION_HEX >= 0x030e0000 - hash += (uint64_t)(PyUnstable_Object_IsUniqueReferencedTemporary(pyobj) ? 1 : 0); -#else - hash += (uint64_t)(Py_REFCNT(pyobj) == 1 ? 1 : 0); -#endif + hash += (uint64_t)(pyobj->ob_refcnt == 1 ? 1 : 0); hash += (hash << 10); hash ^= (hash >> 6); } diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.cxx index 91941883be29e..8ae79432ede0b 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.cxx @@ -105,15 +105,14 @@ static PyObject* meta_getmodule(CPPScope* scope, void*) return CPyCppyy_PyText_FromString(scope->fModuleName); // get C++ representation of outer scope - std::string modname = - TypeManip::extract_namespace(Cppyy::GetScopedFinalName(scope->fCppType)); - if (modname.empty()) + Cppyy::TCppScope_t parent_scope = Cppyy::GetParentScope(scope->fCppType); + if (parent_scope == Cppyy::GetGlobalScope()) return CPyCppyy_PyText_FromString(const_cast("cppyy.gbl")); // now peel scopes one by one, pulling in the python naming (which will // simply recurse if not overridden in python) PyObject* pymodule = nullptr; - PyObject* pyscope = CPyCppyy::GetScopeProxy(Cppyy::GetScope(modname)); + PyObject* pyscope = CPyCppyy::GetScopeProxy(parent_scope); if (pyscope) { // get the module of our module pymodule = PyObject_GetAttr(pyscope, PyStrings::gModule); @@ -133,6 +132,7 @@ static PyObject* meta_getmodule(CPPScope* scope, void*) PyErr_Clear(); // lookup through python failed, so simply cook up a '::' -> '.' replacement + std::string modname = Cppyy::GetScopedFinalName(parent_scope); TypeManip::cppscope_to_pyscope(modname); return CPyCppyy_PyText_FromString(("cppyy.gbl."+modname).c_str()); } @@ -258,7 +258,6 @@ static PyObject* pt_new(PyTypeObject* subtype, PyObject* args, PyObject* kwds) // so make it accessible (the __cpp_cross__ data member also signals that // this is a cross-inheritance class) PyObject* bname = CPyCppyy_PyText_FromString(Cppyy::GetBaseName(result->fCppType, 0).c_str()); - PyErr_Clear(); if (PyObject_SetAttrString((PyObject*)result, "__cpp_cross__", bname) == -1) PyErr_Clear(); Py_DECREF(bname); @@ -275,8 +274,8 @@ static PyObject* pt_new(PyTypeObject* subtype, PyObject* args, PyObject* kwds) // maps for using namespaces and tracking objects if (!Cppyy::IsNamespace(result->fCppType)) { - static Cppyy::TCppType_t exc_type = (Cppyy::TCppType_t)Cppyy::GetScope("std::exception"); - if (Cppyy::IsSubtype(result->fCppType, exc_type)) + static Cppyy::TCppType_t exc_type = (Cppyy::TCppType_t)Cppyy::GetScope("exception", Cppyy::GetScope("std")); + if (Cppyy::IsSubclass(result->fCppType, exc_type)) result->fFlags |= CPPScope::kIsException; if (!(result->fFlags & CPPScope::kIsPython)) result->fImp.fCppObjects = new CppToPyMap_t; @@ -330,6 +329,8 @@ static PyObject* meta_getattro(PyObject* pyclass, PyObject* pyname) attr = nullptr; } PyErr_Clear(); + } else if (CPPScope_Check(attr) && CPPScope_Check(pyclass) && ((CPPScope*)attr)->fFlags & CPPScope::kIsException) { + return CreateExcScopeProxy(attr, pyname, pyclass); } else return attr; } @@ -361,16 +362,16 @@ static PyObject* meta_getattro(PyObject* pyclass, PyObject* pyname) // namespaces may have seen updates in their list of global functions, which // are available as "methods" even though they're not really that - if (klass->fFlags & CPPScope::kIsNamespace) { + if ((klass->fFlags & CPPScope::kIsNamespace) || + scope == Cppyy::GetGlobalScope()) { // tickle lazy lookup of functions - const std::vector methods = - Cppyy::GetMethodIndicesFromName(scope, name); + const std::vector methods = + Cppyy::GetMethodsFromName(scope, name); if (!methods.empty()) { // function exists, now collect overloads std::vector overloads; - for (auto idx : methods) { - overloads.push_back( - new CPPFunction(scope, Cppyy::GetMethod(scope, idx))); + for (auto method : methods) { + overloads.push_back(new CPPFunction(scope, method)); } // Note: can't re-use Utility::AddClass here, as there's the risk of @@ -382,28 +383,21 @@ static PyObject* meta_getattro(PyObject* pyclass, PyObject* pyname) attr = (PyObject*)CPPOverload_New(name, overloads); templated_functions_checked = true; } - - // tickle lazy lookup of data members - if (!attr) { - Cppyy::TCppIndex_t dmi = Cppyy::GetDatamemberIndex(scope, name); - if (dmi != (Cppyy::TCppIndex_t)-1) attr = (PyObject*)CPPDataMember_New(scope, dmi); - } } - // this may be a typedef that resolves to a sugared type if (!attr) { - const std::string& lookup = Cppyy::GetScopedFinalName(klass->fCppType) + "::" + name; - const std::string& resolved = Cppyy::ResolveName(lookup); - if (resolved != lookup) { - const std::string& cpd = TypeManip::compound(resolved); + Cppyy::TCppScope_t lookup_result = Cppyy::GetNamed(name, scope); + if (Cppyy::IsVariable(lookup_result) || Cppyy::IsEnumConstant(lookup_result)) { + attr = (PyObject*)CPPDataMember_New(scope, lookup_result); + } else if (Cppyy::IsTypedefed(lookup_result)) { + Cppyy::TCppType_t resolved_type = Cppyy::ResolveType(Cppyy::GetTypeFromScope(lookup_result)); + const std::string& cpd = TypeManip::compound(Cppyy::GetTypeAsString(resolved_type)); if (cpd == "*") { - const std::string& clean = TypeManip::clean_type(resolved, false, true); - Cppyy::TCppType_t tcl = Cppyy::GetScope(clean); + Cppyy::TCppScope_t tcl = Cppyy::GetScopeFromType(Cppyy::ResolveType(resolved_type)); if (tcl) { typedefpointertoclassobject* tpc = - PyObject_GC_New(typedefpointertoclassobject, &TypedefPointerToClass_Type); + PyObject_New(typedefpointertoclassobject, &TypedefPointerToClass_Type); tpc->fCppType = tcl; - tpc->fDict = PyDict_New(); attr = (PyObject*)tpc; } } @@ -424,11 +418,10 @@ static PyObject* meta_getattro(PyObject* pyclass, PyObject* pyname) // enums types requested as type (rather than the constants) if (!attr) { - // TODO: IsEnum should deal with the scope, using klass->GetListOfEnums()->FindObject() - const std::string& ename = scope == Cppyy::gGlobalScope ? name : Cppyy::GetScopedFinalName(scope)+"::"+name; - if (Cppyy::IsEnum(ename)) { + Cppyy::TCppScope_t enumerator = Cppyy::GetUnderlyingScope(Cppyy::GetNamed(name, scope)); + if (Cppyy::IsEnumScope(enumerator)) { // enum types (incl. named and class enums) - attr = (PyObject*)CPPEnum_New(name, scope); + attr = (PyObject*)CPPEnum_New(name, enumerator); } else { // for completeness in error reporting PyErr_Format(PyExc_TypeError, "\'%s\' is not a known C++ enum", name.c_str()); @@ -440,7 +433,9 @@ static PyObject* meta_getattro(PyObject* pyclass, PyObject* pyname) // cache the result if (CPPDataMember_Check(attr)) { PyType_Type.tp_setattro((PyObject*)Py_TYPE(pyclass), pyname, attr); + Py_DECREF(attr); + // The call below goes through "dm_get" attr = PyType_Type.tp_getattro(pyclass, pyname); if (!attr && PyErr_Occurred()) Utility::FetchError(errors); @@ -505,11 +500,15 @@ static PyObject* meta_getattro(PyObject* pyclass, PyObject* pyname) } if (attr) { + std::for_each(errors.begin(), errors.end(), Utility::PyError_t::Clear); PyErr_Clear(); } else { // not found: prepare a full error report PyObject* topmsg = nullptr; + PyObject* pytype = 0, *pyvalue = 0, *pytrace = 0; + PyErr_Fetch(&pytype, &pyvalue, &pytrace); PyObject* sklass = PyObject_Str(pyclass); + PyErr_Restore(pytype, pyvalue, pytrace); if (sklass) { topmsg = CPyCppyy_PyText_FromFormat("%s has no attribute \'%s\'. Full details:", CPyCppyy_PyText_AsString(sklass), CPyCppyy_PyText_AsString(pyname)); @@ -518,7 +517,7 @@ static PyObject* meta_getattro(PyObject* pyclass, PyObject* pyname) topmsg = CPyCppyy_PyText_FromFormat("no such attribute \'%s\'. Full details:", CPyCppyy_PyText_AsString(pyname)); } - SetDetailedException(std::move(errors), topmsg /* steals */, PyExc_AttributeError /* default error */); + SetDetailedException(errors, topmsg /* steals */, PyExc_AttributeError /* default error */); } return attr; @@ -532,16 +531,13 @@ static int meta_setattro(PyObject* pyclass, PyObject* pyname, PyObject* pyval) // the C++ side, b/c there is no descriptor yet. This triggers the creation for // for such data as necessary. The many checks to narrow down the specific case // are needed to prevent unnecessary lookups and recursion. - if (((CPPScope*)pyclass)->fFlags & CPPScope::kIsNamespace) { // skip if the given pyval is a descriptor already, or an unassignable class - if (!CPyCppyy::CPPDataMember_Check(pyval) && !CPyCppyy::CPPScope_Check(pyval)) { - std::string name = CPyCppyy_PyText_AsString(pyname); - Cppyy::TCppIndex_t dmi = Cppyy::GetDatamemberIndex(((CPPScope*)pyclass)->fCppType, name); - if (dmi != (Cppyy::TCppIndex_t)-1) - meta_getattro(pyclass, pyname); // triggers creation - } + if (!CPyCppyy::CPPDataMember_Check(pyval) && !CPyCppyy::CPPScope_Check(pyval)) { + std::string name = CPyCppyy_PyText_AsString(pyname); + if (Cppyy::GetNamed(name, ((CPPScope*)pyclass)->fCppType)) + meta_getattro(pyclass, pyname); // triggers creation } - PyErr_Clear(); // creation might have failed + return PyType_Type.tp_setattro(pyclass, pyname, pyval); } diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.h b/bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.h index 7a6959fa260e6..6aecfdca05b9c 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.h @@ -51,9 +51,9 @@ class CPPScope { kNoPrettyPrint = 0x0400 }; public: - PyHeapTypeObject fType; - Cppyy::TCppType_t fCppType; - uint32_t fFlags; + PyHeapTypeObject fType; + Cppyy::TCppScope_t fCppType; + uint32_t fFlags; union { CppToPyMap_t* fCppObjects; // classes only std::vector* fUsing; // namespaces only @@ -69,7 +69,7 @@ typedef CPPScope CPPClass; class CPPSmartClass : public CPPClass { public: - Cppyy::TCppType_t fUnderlyingType; + Cppyy::TCppScope_t fUnderlyingType; Cppyy::TCppMethod_t fDereferencer; }; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPyCppyyModule.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPyCppyyModule.cxx index e4b53a2081637..fc0052d18aa41 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPyCppyyModule.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPyCppyyModule.cxx @@ -36,6 +36,8 @@ PyObject* Instance_FromVoidPtr( #include +std::map TypeReductionMap; + // Note: as of py3.11, dictionary objects no longer carry a function pointer for // the lookup, so it can no longer be shimmed and "from cppyy.interactive import *" // thus no longer works. @@ -222,13 +224,23 @@ static PyTypeObject PyDefault_t_Type = { namespace { -struct { - PyObject_HEAD -} _CPyCppyy_NullPtrStruct{PyObject_HEAD_INIT(&PyNullPtr_t_Type)}; +PyObject _CPyCppyy_NullPtrStruct = {_PyObject_EXTRA_INIT +// In 3.12.0-beta this field was changed from a ssize_t to a union +#if PY_VERSION_HEX >= 0x30c00b1 + {1}, +#else + 1, +#endif + &PyNullPtr_t_Type}; -struct { - PyObject_HEAD -} _CPyCppyy_DefaultStruct{PyObject_HEAD_INIT(&PyDefault_t_Type)}; +PyObject _CPyCppyy_DefaultStruct = {_PyObject_EXTRA_INIT +// In 3.12.0-beta this field was changed from a ssize_t to a union +#if PY_VERSION_HEX >= 0x30c00b1 + {1}, +#else + 1, +#endif + &PyDefault_t_Type}; // TODO: refactor with Converters.cxx struct CPyCppyy_tagCDataObject { // non-public (but stable) @@ -447,7 +459,7 @@ static PyObject* SetCppLazyLookup(PyObject*, PyObject* args) } //---------------------------------------------------------------------------- -static PyObject* MakeCppTemplateClass(PyObject*, PyObject* args) +static PyObject* MakeCppTemplateClass(PyObject* /* self */, PyObject* args) { // Create a binding for a templated class instantiation. @@ -457,14 +469,31 @@ static PyObject* MakeCppTemplateClass(PyObject*, PyObject* args) PyErr_Format(PyExc_TypeError, "too few arguments for template instantiation"); return nullptr; } + PyObject *cppscope = PyTuple_GET_ITEM(args, 0); + void * tmpl = PyLong_AsVoidPtr(cppscope); // build "< type, type, ... >" part of class name (modifies pyname) - const std::string& tmpl_name = - Utility::ConstructTemplateArgs(PyTuple_GET_ITEM(args, 0), args, nullptr, Utility::kNone, 1); - if (!tmpl_name.size()) - return nullptr; + std::vector types = + Utility::GetTemplateArgsTypes(cppscope, args, nullptr, Utility::kNone, 1); + if (PyErr_Occurred()) + return nullptr; + + Cppyy::TCppScope_t scope = + Cppyy::InstantiateTemplate(tmpl, types.data(), types.size()); + for (Cpp::TemplateArgInfo i: types) { + if (i.m_IntegralValue) + std::free((void*)i.m_IntegralValue); + } - return CreateScopeProxy(tmpl_name); + if (!scope) { + PyErr_Format(PyExc_TypeError, + "Template instantiation failed: '%s' with args: '%s\n'", + Cppyy::GetScopedFinalName(cppscope).c_str(), + CPyCppyy_PyText_AsString(PyObject_Repr(args))); + return nullptr; + } + + return CreateScopeProxy(scope); } //---------------------------------------------------------------------------- @@ -673,7 +702,7 @@ static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) } // convert 2nd argument first (used for both pointer value and instance cases) - Cppyy::TCppType_t cast_type = 0; + Cppyy::TCppScope_t cast_type = 0; PyObject* arg1 = PyTuple_GET_ITEM(args, 1); if (!CPyCppyy_PyText_Check(arg1)) { // not string, then class if (CPPScope_Check(arg1)) @@ -684,7 +713,7 @@ static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) Py_INCREF(arg1); if (!cast_type && arg1) { - cast_type = (Cppyy::TCppType_t)Cppyy::GetScope(CPyCppyy_PyText_AsString(arg1)); + cast_type = (Cppyy::TCppScope_t)Cppyy::GetScope(CPyCppyy_PyText_AsString(arg1)); Py_DECREF(arg1); } @@ -701,7 +730,7 @@ static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) // if this instance's class has a relation to the requested one, calculate the // offset, erase if from any caches, and update the pointer and type CPPInstance* arg0_pyobj = (CPPInstance*)arg0; - Cppyy::TCppType_t cur_type = arg0_pyobj->ObjectIsA(false /* check_smart */); + Cppyy::TCppScope_t cur_type = arg0_pyobj->ObjectIsA(false /* check_smart */); bool isPython = CPPScope_Check(arg1) && \ (((CPPClass*)arg1)->fFlags & CPPScope::kIsPython); @@ -712,12 +741,12 @@ static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) } int direction = 0; - Cppyy::TCppType_t base = 0, derived = 0; - if (Cppyy::IsSubtype(cast_type, cur_type)) { + Cppyy::TCppScope_t base = 0, derived = 0; + if (Cppyy::IsSubclass(cast_type, cur_type)) { derived = cast_type; base = cur_type; direction = -1; // down-cast - } else if (Cppyy::IsSubtype(cur_type, cast_type)) { + } else if (Cppyy::IsSubclass(cur_type, cast_type)) { base = cast_type; derived = cur_type; direction = 1; // up-cast @@ -777,7 +806,7 @@ static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) // not a pre-existing object; get the address and bind void* addr = nullptr; - if (arg0 != gNullPtrObject) { + if (arg0 != &_CPyCppyy_NullPtrStruct) { addr = CPyCppyy_PyCapsule_GetPointer(arg0, nullptr); if (PyErr_Occurred()) { PyErr_Clear(); @@ -891,7 +920,9 @@ static PyObject* AddTypeReducer(PyObject*, PyObject* args) if (!PyArg_ParseTuple(args, const_cast("ss"), &reducable, &reduced)) return nullptr; - Cppyy::AddTypeReducer(reducable, reduced); + Cppyy::TCppType_t reducable_type = Cppyy::GetTypeFromScope(Cppyy::GetScope(reducable)); + Cppyy::TCppType_t reduced_type = Cppyy::GetTypeFromScope(Cppyy::GetScope(reduced)); + TypeReductionMap[reducable_type] = reduced_type; Py_RETURN_NONE; } @@ -1051,6 +1082,8 @@ static struct PyModuleDef moduledef = { }; #endif + +#define CPYCPPYY_INIT_ERROR return nullptr namespace CPyCppyy { //---------------------------------------------------------------------------- @@ -1089,7 +1122,7 @@ PyObject* Init() gThisModule = Py_InitModule(const_cast("libcppyy"), gCPyCppyyMethods); #endif if (!gThisModule) - return nullptr; + CPYCPPYY_INIT_ERROR; // keep gThisModule, but do not increase its reference count even as it is borrowed, // or a self-referencing cycle would be created @@ -1103,58 +1136,58 @@ PyObject* Init() // inject meta type if (!Utility::InitProxy(gThisModule, &CPPScope_Type, "CPPScope")) - return nullptr; + CPYCPPYY_INIT_ERROR; // inject object proxy type if (!Utility::InitProxy(gThisModule, &CPPInstance_Type, "CPPInstance")) - return nullptr; + CPYCPPYY_INIT_ERROR; // inject exception object proxy type if (!Utility::InitProxy(gThisModule, &CPPExcInstance_Type, "CPPExcInstance")) - return nullptr; + CPYCPPYY_INIT_ERROR; // inject method proxy type if (!Utility::InitProxy(gThisModule, &CPPOverload_Type, "CPPOverload")) - return nullptr; + CPYCPPYY_INIT_ERROR; // inject template proxy type if (!Utility::InitProxy(gThisModule, &TemplateProxy_Type, "TemplateProxy")) - return nullptr; + CPYCPPYY_INIT_ERROR; // inject property proxy type if (!Utility::InitProxy(gThisModule, &CPPDataMember_Type, "CPPDataMember")) - return nullptr; + CPYCPPYY_INIT_ERROR; // inject custom data types #if PY_VERSION_HEX < 0x03000000 if (!Utility::InitProxy(gThisModule, &RefFloat_Type, "Double")) - return nullptr; + CPYCPPYY_INIT_ERROR; if (!Utility::InitProxy(gThisModule, &RefInt_Type, "Long")) - return nullptr; + CPYCPPYY_INIT_ERROR; #endif if (!Utility::InitProxy(gThisModule, &CustomInstanceMethod_Type, "InstanceMethod")) - return nullptr; + CPYCPPYY_INIT_ERROR; if (!Utility::InitProxy(gThisModule, &TupleOfInstances_Type, "InstanceArray")) - return nullptr; + CPYCPPYY_INIT_ERROR; if (!Utility::InitProxy(gThisModule, &LowLevelView_Type, "LowLevelView")) - return nullptr; + CPYCPPYY_INIT_ERROR; if (!Utility::InitProxy(gThisModule, &PyNullPtr_t_Type, "nullptr_t")) - return nullptr; + CPYCPPYY_INIT_ERROR; // custom iterators if (PyType_Ready(&InstanceArrayIter_Type) < 0) - return nullptr; + CPYCPPYY_INIT_ERROR; if (PyType_Ready(&IndexIter_Type) < 0) - return nullptr; + CPYCPPYY_INIT_ERROR; if (PyType_Ready(&VectorIter_Type) < 0) - return nullptr; + CPYCPPYY_INIT_ERROR; // inject identifiable nullptr and default gNullPtrObject = (PyObject*)&_CPyCppyy_NullPtrStruct; @@ -1178,6 +1211,12 @@ PyObject* Init() gAbrtException = PyErr_NewException((char*)"cppyy.ll.AbortSignal", cppfatal, nullptr); PyModule_AddObject(gThisModule, (char*)"AbortSignal", gAbrtException); +// policy labels + PyModule_AddObject(gThisModule, (char*)"kMemoryHeuristics", + PyInt_FromLong((int)CallContext::kUseHeuristics)); + PyModule_AddObject(gThisModule, (char*)"kMemoryStrict", + PyInt_FromLong((int)CallContext::kUseStrict)); + // gbl namespace is injected in cppyy.py // create the memory regulator @@ -1185,8 +1224,8 @@ PyObject* Init() #if PY_VERSION_HEX >= 0x03000000 Py_INCREF(gThisModule); -#endif return gThisModule; +#endif } } // namespace CPyCppyy diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CallContext.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CallContext.cxx index 416741d0caab1..794c2e4ed294c 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CallContext.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CallContext.cxx @@ -9,6 +9,15 @@ uint32_t &CPyCppyy::CallContext::GlobalPolicyFlags() return flags; } +//- data _____________________________________________________________________ +namespace CPyCppyy { + + CallContext::ECallFlags CallContext::sMemoryPolicy = CallContext::kUseStrict; +// this is just a data holder for linking; actual value is set in CPyCppyyModule.cxx + CallContext::ECallFlags CallContext::sSignalPolicy = CallContext::kNone; + +} // namespace CPyCppyy + //----------------------------------------------------------------------------- void CPyCppyy::CallContext::AddTemporary(PyObject* pyobj) { if (pyobj) { @@ -35,6 +44,26 @@ void CPyCppyy::CallContext::Cleanup() { } //----------------------------------------------------------------------------- +bool CPyCppyy::CallContext::SetMemoryPolicy(ECallFlags e) +{ +// Set the global memory policy, which affects object ownership when objects +// are passed as function arguments. + if (kUseHeuristics == e || e == kUseStrict) { + sMemoryPolicy = e; + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +bool CPyCppyy::CallContext::SetGlobalSignalPolicy(bool setProtected) +{ +// Set the global signal policy, which determines whether a jmp address +// should be saved to return to after a C++ segfault. + bool old = sSignalPolicy == kProtected; + sSignalPolicy = setProtected ? kProtected : kNone; + return old; +} bool CPyCppyy::CallContext::SetGlobalPolicy(ECallFlags toggleFlag, bool enabled) { auto &flags = GlobalPolicyFlags(); diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CallContext.h b/bindings/pyroot/cppyy/CPyCppyy/src/CallContext.h index 4b88bfdd3a6fb..da15d997ff41b 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CallContext.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CallContext.h @@ -69,6 +69,7 @@ struct CallContext { kUseHeuristics = 0x000100, // if method applies heuristics memory policy kImplicitSmartPtrConversion = 0x000200, // enable implicit conversion to smart pointers kReleaseGIL = 0x000400, // if method should release the GIL + kUseStrict = 0x000600, // if method applies strict memory policy kSetLifeLine = 0x000800, // if return value is part of 'this' kNeverLifeLine = 0x001000, // if the return value is never part of 'this' kPyException = 0x002000, // Python exception during method execution @@ -78,6 +79,10 @@ struct CallContext { kIsPseudoFunc = 0x020000, // internal, used for introspection }; +// memory handling + static ECallFlags sMemoryPolicy; + static bool SetMemoryPolicy(ECallFlags e); + // Policies about memory handling and signal safety static bool SetGlobalPolicy(ECallFlags e, bool enabled); @@ -86,6 +91,10 @@ struct CallContext { void AddTemporary(PyObject* pyobj); void Cleanup(); +// signal safety + static ECallFlags sSignalPolicy; + static bool SetGlobalSignalPolicy(bool setProtected); + Parameter* GetArgs(size_t sz) { if (sz != (size_t)-1) fNArgs = sz; if (fNArgs <= SMALL_ARGS_N) return fArgs; @@ -151,6 +160,15 @@ inline bool UseStrictOwnership() { return !(CC::GlobalPolicyFlags() & CC::kUseHeuristics); } +inline bool UseStrictOwnership(CallContext* ctxt) { + if (ctxt && (ctxt->fFlags & CallContext::kUseStrict)) + return true; + if (ctxt && (ctxt->fFlags & CallContext::kUseHeuristics)) + return false; + + return CallContext::sMemoryPolicy == CallContext::kUseStrict; +} + template class CallContextRAII { public: diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Converters.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/Converters.cxx index e651c2a29228f..fef3014daeb38 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Converters.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Converters.cxx @@ -61,9 +61,6 @@ namespace CPyCppyy { static std::regex s_fnptr("\\((\\w*:*)*\\*&*\\)"); } -// Define our own PyUnstable_Object_IsUniqueReferencedTemporary function if the -// Python version is lower than 3.14, the version where that function got introduced. -#if PY_VERSION_HEX < 0x030e0000 #if PY_VERSION_HEX < 0x03000000 const Py_ssize_t MOVE_REFCOUNT_CUTOFF = 1; #elif PY_VERSION_HEX < 0x03080000 @@ -76,10 +73,6 @@ const Py_ssize_t MOVE_REFCOUNT_CUTOFF = 2; // since py3.8, vector calls behave again as expected const Py_ssize_t MOVE_REFCOUNT_CUTOFF = 1; #endif -inline bool PyUnstable_Object_IsUniqueReferencedTemporary(PyObject *pyobject) { - return Py_REFCNT(pyobject) <= MOVE_REFCOUNT_CUTOFF; -} -#endif //- pretend-ctypes helpers --------------------------------------------------- struct CPyCppyy_tagCDataObject { // non-public (but stable) @@ -131,9 +124,7 @@ struct CPyCppyy_tagPyCArgObject { // not public (but stable; note that olde #define ct_c_complex 22 #define ct_c_pointer 23 #define ct_c_funcptr 24 -#define ct_c_int16 25 -#define ct_c_int32 26 -#define NTYPES 27 +#define NTYPES 25 static std::array gCTypesNames = { "c_bool", "c_char", "c_wchar", "c_byte", "c_ubyte", "c_short", "c_ushort", "c_uint16", @@ -398,10 +389,6 @@ static inline type CPyCppyy_PyLong_As##name(PyObject* pyobject) \ CPPYY_PYLONG_AS_TYPE(UInt8, uint8_t, 0, UCHAR_MAX) CPPYY_PYLONG_AS_TYPE(Int8, int8_t, SCHAR_MIN, SCHAR_MAX) -CPPYY_PYLONG_AS_TYPE(UInt16, uint16_t, 0, UINT16_MAX) -CPPYY_PYLONG_AS_TYPE(Int16, int16_t, INT16_MIN, INT16_MAX) -CPPYY_PYLONG_AS_TYPE(UInt32, uint32_t, 0, UINT32_MAX) -CPPYY_PYLONG_AS_TYPE(Int32, int32_t, INT32_MIN, INT32_MAX) CPPYY_PYLONG_AS_TYPE(UShort, unsigned short, 0, USHRT_MAX) CPPYY_PYLONG_AS_TYPE(Short, short, SHRT_MIN, SHRT_MAX) CPPYY_PYLONG_AS_TYPE(StrictInt, int, INT_MIN, INT_MAX) @@ -550,9 +537,10 @@ bool CPyCppyy::Converter::ToMemory(PyObject*, void*, PyObject* /* ctxt */) if (val == (type)-1 && PyErr_Occurred()) { \ static PyTypeObject* ctypes_type = nullptr; \ if (!ctypes_type) { \ - auto error = CPyCppyy::Utility::FetchPyError(); \ + PyObject* pytype = 0, *pyvalue = 0, *pytrace = 0; \ + PyErr_Fetch(&pytype, &pyvalue, &pytrace); \ ctypes_type = GetCTypesType(ct_##ctype); \ - CPyCppyy::Utility::RestorePyError(error); \ + PyErr_Restore(pytype, pyvalue, pytrace); \ } \ if (Py_TYPE(pyobject) == ctypes_type) { \ PyErr_Clear(); \ @@ -797,10 +785,6 @@ CPPYY_IMPL_BASIC_CONST_CHAR_REFCONVERTER(UChar, unsigned char, c_uchar, 0 CPPYY_IMPL_BASIC_CONST_REFCONVERTER(Bool, bool, c_bool, CPyCppyy_PyLong_AsBool) CPPYY_IMPL_BASIC_CONST_REFCONVERTER(Int8, int8_t, c_int8, CPyCppyy_PyLong_AsInt8) CPPYY_IMPL_BASIC_CONST_REFCONVERTER(UInt8, uint8_t, c_uint8, CPyCppyy_PyLong_AsUInt8) -CPPYY_IMPL_BASIC_CONST_REFCONVERTER(Int16, int16_t, c_int16, CPyCppyy_PyLong_AsInt16) -CPPYY_IMPL_BASIC_CONST_REFCONVERTER(UInt16, uint16_t, c_uint16, CPyCppyy_PyLong_AsUInt16) -CPPYY_IMPL_BASIC_CONST_REFCONVERTER(Int32, int32_t, c_int32, CPyCppyy_PyLong_AsInt32) -CPPYY_IMPL_BASIC_CONST_REFCONVERTER(UInt32, uint32_t, c_uint32, CPyCppyy_PyLong_AsUInt32) CPPYY_IMPL_BASIC_CONST_REFCONVERTER(Short, short, c_short, CPyCppyy_PyLong_AsShort) CPPYY_IMPL_BASIC_CONST_REFCONVERTER(UShort, unsigned short, c_ushort, CPyCppyy_PyLong_AsUShort) CPPYY_IMPL_BASIC_CONST_REFCONVERTER(Int, int, c_int, CPyCppyy_PyLong_AsStrictInt) @@ -876,10 +860,6 @@ CPPYY_IMPL_REFCONVERTER(SChar, c_byte, signed char, 'b'); CPPYY_IMPL_REFCONVERTER(UChar, c_ubyte, unsigned char, 'B'); CPPYY_IMPL_REFCONVERTER(Int8, c_int8, int8_t, 'b'); CPPYY_IMPL_REFCONVERTER(UInt8, c_uint8, uint8_t, 'B'); -CPPYY_IMPL_REFCONVERTER(Int16, c_int16, int16_t, 'h'); -CPPYY_IMPL_REFCONVERTER(UInt16, c_uint16, uint16_t, 'H'); -CPPYY_IMPL_REFCONVERTER(Int32, c_int32, int32_t, 'i'); -CPPYY_IMPL_REFCONVERTER(UInt32, c_uint32, uint32_t, 'I'); CPPYY_IMPL_REFCONVERTER(Short, c_short, short, 'h'); CPPYY_IMPL_REFCONVERTER(UShort, c_ushort, unsigned short, 'H'); CPPYY_IMPL_REFCONVERTER_FROM_MEMORY(Int, c_int); @@ -1038,14 +1018,6 @@ CPPYY_IMPL_BASIC_CONVERTER_IB( Int8, int8_t, long, c_int8, PyInt_FromLong, CPyCppyy_PyLong_AsInt8, 'l') CPPYY_IMPL_BASIC_CONVERTER_IB( UInt8, uint8_t, long, c_uint8, PyInt_FromLong, CPyCppyy_PyLong_AsUInt8, 'l') -CPPYY_IMPL_BASIC_CONVERTER_IB( - Int16, int16_t, long, c_int16, PyInt_FromLong, CPyCppyy_PyLong_AsInt16, 'l') -CPPYY_IMPL_BASIC_CONVERTER_IB( - UInt16, uint16_t, long, c_uint16, PyInt_FromLong, CPyCppyy_PyLong_AsUInt16, 'l') -CPPYY_IMPL_BASIC_CONVERTER_IB( - Int32, int32_t, long, c_int32, PyInt_FromLong, CPyCppyy_PyLong_AsInt32, 'l') -CPPYY_IMPL_BASIC_CONVERTER_IB( - UInt32, uint32_t, long, c_uint32, PyInt_FromLong, CPyCppyy_PyLong_AsUInt32, 'l') CPPYY_IMPL_BASIC_CONVERTER_IB( Short, short, long, c_short, PyInt_FromLong, CPyCppyy_PyLong_AsShort, 'l') CPPYY_IMPL_BASIC_CONVERTER_IB( @@ -1122,7 +1094,7 @@ CPPYY_IMPL_BASIC_CONVERTER_NB( LDouble, PY_LONG_DOUBLE, PY_LONG_DOUBLE, c_longdouble, PyFloat_FromDouble, PyFloat_AsDouble, 'g') CPyCppyy::ComplexDConverter::ComplexDConverter(bool keepControl) : - InstanceConverter(Cppyy::GetScope("std::complex"), keepControl) {} + InstanceConverter(Cppyy::GetFullScope("std::complex"), keepControl) {} // special case for std::complex, maps it to/from Python's complex bool CPyCppyy::ComplexDConverter::SetArg( @@ -1181,7 +1153,7 @@ bool CPyCppyy::DoubleRefConverter::SetArg( // alternate, pass pointer from buffer Py_ssize_t buflen = Utility::GetBuffer(pyobject, 'd', sizeof(double), para.fValue.fVoidp); - if (para.fValue.fVoidp && buflen) { + if (buflen && para.fValue.fVoidp) { para.fTypeCode = 'V'; return true; } @@ -1288,14 +1260,16 @@ bool CPyCppyy::CStringConverter::SetArg( const char* cstr = CPyCppyy_PyText_AsStringAndSize(pyobject, &len); if (!cstr) { // special case: allow ctypes c_char_p - auto error = CPyCppyy::Utility::FetchPyError(); + PyObject* pytype = 0, *pyvalue = 0, *pytrace = 0; + PyErr_Fetch(&pytype, &pyvalue, &pytrace); if (Py_TYPE(pyobject) == GetCTypesType(ct_c_char_p)) { SetLifeLine(ctxt->fPyContext, pyobject, (intptr_t)this); para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr; para.fTypeCode = 'V'; + Py_XDECREF(pytype); Py_XDECREF(pyvalue); Py_XDECREF(pytrace); return true; } - CPyCppyy::Utility::RestorePyError(error); + PyErr_Restore(pytype, pyvalue, pytrace); return false; } @@ -1549,13 +1523,14 @@ bool CPyCppyy::VoidArrayConverter::GetAddressSpecialCase(PyObject* pyobject, voi //---------------------------------------------------------------------------- bool CPyCppyy::VoidArrayConverter::SetArg( - PyObject* pyobject, Parameter& para, CallContext* /*ctxt*/) + PyObject* pyobject, Parameter& para, CallContext* ctxt) { // just convert pointer if it is a C++ object CPPInstance* pyobj = GetCppInstance(pyobject); + para.fValue.fVoidp = nullptr; if (pyobj) { // depending on memory policy, some objects are no longer owned when passed to C++ - if (!fKeepControl && !UseStrictOwnership()) + if (!fKeepControl && !UseStrictOwnership(ctxt)) pyobj->CppOwns(); // set pointer (may be null) and declare success @@ -1620,7 +1595,7 @@ bool CPyCppyy::VoidArrayConverter::ToMemory(PyObject* value, void* address, PyOb CPPInstance* pyobj = GetCppInstance(value); if (pyobj) { // depending on memory policy, some objects are no longer owned when passed to C++ - if (!fKeepControl && !UseStrictOwnership()) + if (!fKeepControl && CallContext::sMemoryPolicy != CallContext::kUseStrict) pyobj->CppOwns(); // set pointer (may be null) and declare success @@ -1645,42 +1620,6 @@ bool CPyCppyy::VoidArrayConverter::ToMemory(PyObject* value, void* address, PyOb return true; } -namespace { - -// Copy a buffer to memory address with an array converter. -template -bool ToArrayFromBuffer(PyObject* owner, void* address, PyObject* ctxt, - const void * buf, Py_ssize_t buflen, - CPyCppyy::dims_t& shape, bool isFixed) -{ - if (buflen == 0) - return false; - - Py_ssize_t oldsz = 1; - for (Py_ssize_t idim = 0; idim < shape.ndim(); ++idim) { - if (shape[idim] == CPyCppyy::UNKNOWN_SIZE) { - oldsz = -1; - break; - } - oldsz *= shape[idim]; - } - if (shape.ndim() != CPyCppyy::UNKNOWN_SIZE && 0 < oldsz && oldsz < buflen) { - PyErr_SetString(PyExc_ValueError, "buffer too large for value"); - return false; - } - - if (isFixed) - memcpy(*(type**)address, buf, (0 < buflen ? buflen : 1)*sizeof(type)); - else { - *(type**)address = (type*)buf; - shape.ndim(1); - shape[0] = buflen; - SetLifeLine(ctxt, owner, (intptr_t)address); - } - return true; -} - -} //---------------------------------------------------------------------------- #define CPPYY_IMPL_ARRAY_CONVERTER(name, ctype, type, code, suffix) \ @@ -1761,7 +1700,31 @@ bool CPyCppyy::name##ArrayConverter::ToMemory( \ if (fShape.ndim() <= 1 || fIsFixed) { \ void* buf = nullptr; \ Py_ssize_t buflen = Utility::GetBuffer(value, code, sizeof(type), buf);\ - return ToArrayFromBuffer(value, address, ctxt, buf, buflen, fShape, fIsFixed);\ + if (buflen == 0) \ + return false; \ + \ + Py_ssize_t oldsz = 1; \ + for (Py_ssize_t idim = 0; idim < fShape.ndim(); ++idim) { \ + if (fShape[idim] == UNKNOWN_SIZE) { \ + oldsz = -1; \ + break; \ + } \ + oldsz *= fShape[idim]; \ + } \ + if (fShape.ndim() != UNKNOWN_SIZE && 0 < oldsz && oldsz < buflen) { \ + PyErr_SetString(PyExc_ValueError, "buffer too large for value"); \ + return false; \ + } \ + \ + if (fIsFixed) \ + memcpy(*(type**)address, buf, (0 < buflen ? buflen : 1)*sizeof(type));\ + else { \ + *(type**)address = (type*)buf; \ + fShape.ndim(1); \ + fShape[0] = buflen; \ + SetLifeLine(ctxt, value, (intptr_t)address); \ + } \ + \ } else { /* multi-dim, non-flat array; assume structure matches */ \ void* buf = nullptr; /* TODO: GetBuffer() assumes flat? */ \ Py_ssize_t buflen = Utility::GetBuffer(value, code, sizeof(void*), buf);\ @@ -1781,11 +1744,7 @@ CPPYY_IMPL_ARRAY_CONVERTER(UChar, c_ubyte, unsigned char, 'B', ) CPPYY_IMPL_ARRAY_CONVERTER(Byte, c_ubyte, std::byte, 'B', ) #endif CPPYY_IMPL_ARRAY_CONVERTER(Int8, c_byte, int8_t, 'b', _i8) -CPPYY_IMPL_ARRAY_CONVERTER(Int16, c_int16, int16_t, 'h', _i16) -CPPYY_IMPL_ARRAY_CONVERTER(Int32, c_int32, int32_t, 'i', _i32) CPPYY_IMPL_ARRAY_CONVERTER(UInt8, c_ubyte, uint8_t, 'B', _i8) -CPPYY_IMPL_ARRAY_CONVERTER(UInt16, c_uint16, uint16_t, 'H', _i16) -CPPYY_IMPL_ARRAY_CONVERTER(UInt32, c_uint32, uint32_t, 'I', _i32) CPPYY_IMPL_ARRAY_CONVERTER(Short, c_short, short, 'h', ) CPPYY_IMPL_ARRAY_CONVERTER(UShort, c_ushort, unsigned short, 'H', ) CPPYY_IMPL_ARRAY_CONVERTER(Int, c_int, int, 'i', ) @@ -1864,18 +1823,6 @@ PyObject* CPyCppyy::CStringArrayConverter::FromMemory(void* address) return CreateLowLevelViewString(*(const char***)address, fShape); } -//---------------------------------------------------------------------------- -bool CPyCppyy::CStringArrayConverter::ToMemory(PyObject* value, void* address, PyObject* ctxt) -{ -// As a special array converter, the CStringArrayConverter one can also copy strings in the array, -// and not only buffers. - Py_ssize_t len; - if (const char* cstr = CPyCppyy_PyText_AsStringAndSize(value, &len)) { - return ToArrayFromBuffer(value, address, ctxt, cstr, len, fShape, fIsFixed); - } - return SCharArrayConverter::ToMemory(value, address, ctxt); -} - //---------------------------------------------------------------------------- PyObject* CPyCppyy::NonConstCStringArrayConverter::FromMemory(void* address) { @@ -1929,7 +1876,7 @@ static inline bool CPyCppyy_PyUnicodeAsBytes2Buffer(PyObject* pyobject, T& buffe #define CPPYY_IMPL_STRING_AS_PRIMITIVE_CONVERTER(name, type, F1, F2) \ CPyCppyy::name##Converter::name##Converter(bool keepControl) : \ - InstanceConverter(Cppyy::GetScope(#type), keepControl) {} \ + InstanceConverter(Cppyy::GetFullScope(#type), keepControl) {} \ \ bool CPyCppyy::name##Converter::SetArg( \ PyObject* pyobject, Parameter& para, CallContext* ctxt) \ @@ -1970,7 +1917,7 @@ CPPYY_IMPL_STRING_AS_PRIMITIVE_CONVERTER(STLString, std::string, c_str, size) CPyCppyy::STLWStringConverter::STLWStringConverter(bool keepControl) : - InstanceConverter(Cppyy::GetScope("std::wstring"), keepControl) {} + InstanceConverter(Cppyy::GetFullScope("std::wstring"), keepControl) {} bool CPyCppyy::STLWStringConverter::SetArg( PyObject* pyobject, Parameter& para, CallContext* ctxt) @@ -2034,7 +1981,7 @@ bool CPyCppyy::STLWStringConverter::ToMemory(PyObject* value, void* address, PyO #if (__cplusplus > 201402L) || (defined(_MSC_VER) && _MSVC_LANG > 201402L) CPyCppyy::STLStringViewConverter::STLStringViewConverter(bool keepControl) : - InstanceConverter(Cppyy::GetScope("std::string_view"), keepControl) {} + InstanceConverter(Cppyy::GetFullScope("std::string_view"), keepControl) {} bool CPyCppyy::STLStringViewConverter::SetArg( PyObject* pyobject, Parameter& para, CallContext* ctxt) @@ -2066,7 +2013,7 @@ bool CPyCppyy::STLStringViewConverter::SetArg( // special case of a C++ std::string object; life-time management is left to // the caller to ensure any external changes propagate correctly if (CPPInstance_Check(pyobject)) { - static Cppyy::TCppScope_t sStringID = Cppyy::GetScope("std::string"); + static Cppyy::TCppScope_t sStringID = Cppyy::GetUnderlyingScope(Cppyy::GetFullScope("std::string")); CPPInstance* pyobj = (CPPInstance*)pyobject; if (pyobj->ObjectIsA() == sStringID) { void* ptr = pyobj->GetObject(); @@ -2125,7 +2072,7 @@ bool CPyCppyy::STLStringMoveConverter::SetArg( if (pyobj->fFlags & CPPInstance::kIsRValue) { pyobj->fFlags &= ~CPPInstance::kIsRValue; moveit_reason = 2; - } else if (PyUnstable_Object_IsUniqueReferencedTemporary(pyobject)) { + } else if (pyobject->ob_refcnt <= MOVE_REFCOUNT_CUTOFF) { moveit_reason = 1; } else moveit_reason = 0; @@ -2166,9 +2113,9 @@ bool CPyCppyy::InstancePtrConverter::SetArg( return false; Cppyy::TCppType_t oisa = pyobj->ObjectIsA(); - if (oisa && (oisa == fClass || Cppyy::IsSubtype(oisa, fClass))) { + if (oisa && (oisa == fClass || Cppyy::IsSubclass(oisa, fClass))) { // depending on memory policy, some objects need releasing when passed into functions - if (!KeepControl() && !UseStrictOwnership()) + if (!KeepControl() && !UseStrictOwnership(ctxt)) pyobj->CppOwns(); // calculate offset between formal and actual arguments @@ -2213,9 +2160,9 @@ bool CPyCppyy::InstancePtrConverter::ToMemory(PyObject* value, void* ad return false; } - if (Cppyy::IsSubtype(pyobj->ObjectIsA(), fClass)) { + if (Cppyy::IsSubclass(pyobj->ObjectIsA(), fClass)) { // depending on memory policy, some objects need releasing when passed into functions - if (!KeepControl() && !UseStrictOwnership()) + if (!KeepControl() && CallContext::sMemoryPolicy != CallContext::kUseStrict) ((CPPInstance*)value)->CppOwns(); *(void**)address = pyobj->GetObject(); @@ -2235,7 +2182,10 @@ bool CPyCppyy::InstanceConverter::SetArg( CPPInstance* pyobj = GetCppInstance(pyobject, fClass); if (pyobj) { auto oisa = pyobj->ObjectIsA(); - if (oisa && (oisa == fClass || Cppyy::IsSubtype(oisa, fClass))) { + if (oisa && ((oisa == (Cppyy::IsTypedefed(fClass) + ? Cppyy::GetUnderlyingScope(fClass) + : fClass)) || + Cppyy::IsSubclass(oisa, fClass))) { // calculate offset between formal and actual arguments para.fValue.fVoidp = pyobj->GetObject(); if (!para.fValue.fVoidp) @@ -2302,7 +2252,7 @@ bool CPyCppyy::InstanceRefConverter::SetArg( Cppyy::TCppType_t cls = 0; if (pyobj->IsSmart()) { cls = pyobj->ObjectIsA(false); - if (cls && Cppyy::IsSubtype(cls, fClass)) { + if (cls && Cppyy::IsSubclass(cls, fClass)) { para.fValue.fVoidp = pyobj->GetObjectRaw(); argset = true; } @@ -2310,7 +2260,7 @@ bool CPyCppyy::InstanceRefConverter::SetArg( if (!argset) { cls = pyobj->ObjectIsA(); - if (cls && Cppyy::IsSubtype(cls, fClass)) { + if (cls && Cppyy::IsSubclass(cls, fClass)) { para.fValue.fVoidp = pyobj->GetObject(); argset = true; } @@ -2362,7 +2312,7 @@ bool CPyCppyy::InstanceMoveConverter::SetArg( if (pyobj->fFlags & CPPInstance::kIsRValue) { pyobj->fFlags &= ~CPPInstance::kIsRValue; moveit_reason = 2; - } else if (PyUnstable_Object_IsUniqueReferencedTemporary(pyobject)) { + } else if (pyobject->ob_refcnt <= MOVE_REFCOUNT_CUTOFF) { moveit_reason = 1; } @@ -2380,7 +2330,7 @@ bool CPyCppyy::InstanceMoveConverter::SetArg( //---------------------------------------------------------------------------- template bool CPyCppyy::InstancePtrPtrConverter::SetArg( - PyObject* pyobject, Parameter& para, CallContext* /*ctxt*/) + PyObject* pyobject, Parameter& para, CallContext* ctxt) { // convert to C++ instance**, set arg for call CPPInstance* pyobj = GetCppInstance(pyobject); @@ -2394,9 +2344,9 @@ bool CPyCppyy::InstancePtrPtrConverter::SetArg( return false; // not a cppyy object (TODO: handle SWIG etc.) } - if (Cppyy::IsSubtype(pyobj->ObjectIsA(), fClass)) { + if (Cppyy::IsSubclass(pyobj->ObjectIsA(), fClass)) { // depending on memory policy, some objects need releasing when passed into functions - if (!KeepControl() && !UseStrictOwnership()) + if (!KeepControl() && !UseStrictOwnership(ctxt)) pyobj->CppOwns(); // set pointer (may be null) and declare success @@ -2435,9 +2385,9 @@ bool CPyCppyy::InstancePtrPtrConverter::ToMemory( return false; // not a cppyy object (TODO: handle SWIG etc.) } - if (Cppyy::IsSubtype(pyobj->ObjectIsA(), fClass)) { + if (Cppyy::IsSubclass(pyobj->ObjectIsA(), fClass)) { // depending on memory policy, some objects need releasing when passed into functions - if (!KeepControl() && !UseStrictOwnership()) + if (!KeepControl() && CallContext::sMemoryPolicy != CallContext::kUseStrict) pyobj->CppOwns(); // register the value for potential recycling @@ -2477,7 +2427,7 @@ bool CPyCppyy::InstanceArrayConverter::SetArg( if (!CPPInstance_Check(first)) return false; // should not happen - if (Cppyy::IsSubtype(((CPPInstance*)first)->ObjectIsA(), fClass)) { + if (Cppyy::IsSubclass(((CPPInstance*)first)->ObjectIsA(), fClass)) { // no memory policies supported; set pointer (may be null) and declare success para.fValue.fVoidp = ((CPPInstance*)first)->GetObject(); para.fTypeCode = 'p'; @@ -2539,8 +2489,8 @@ bool CPyCppyy::VoidPtrRefConverter::SetArg( } //---------------------------------------------------------------------------- -CPyCppyy::VoidPtrPtrConverter::VoidPtrPtrConverter(cdims_t dims) : - fShape(dims) { +CPyCppyy::VoidPtrPtrConverter::VoidPtrPtrConverter(cdims_t dims, const std::string &failureMsg) : + fShape(dims), fFailureMsg (failureMsg) { fIsFixed = dims ? fShape[0] != UNKNOWN_SIZE : false; } @@ -2666,6 +2616,12 @@ static void* PyFunction_AsCPointer(PyObject* pyobject, // function pointer. The former is direct, the latter involves a JIT-ed wrapper. static PyObject* sWrapperCacheEraser = PyCFunction_New(&gWrapperCacheEraserMethodDef, nullptr); + // FIXME: avoid string comparisons and parsing + std::string true_signature = signature; + + if (true_signature.rfind("(void)") != std::string::npos) + true_signature = true_signature.substr(0, true_signature.size() - 6) + "()"; + using namespace CPyCppyy; if (CPPOverload_Check(pyobject)) { @@ -2693,7 +2649,7 @@ static void* PyFunction_AsCPointer(PyObject* pyobject, if (pytmpl->fTemplateArgs) fullname += CPyCppyy_PyText_AsString(pytmpl->fTemplateArgs); Cppyy::TCppScope_t scope = ((CPPClass*)pytmpl->fTI->fPyClass)->fCppType; - Cppyy::TCppMethod_t cppmeth = Cppyy::GetMethodTemplate(scope, fullname, signature); + Cppyy::TCppMethod_t cppmeth = Cppyy::GetMethodTemplate(scope, fullname, true_signature); if (cppmeth) { void* fptr = (void*)Cppyy::GetFunctionAddress(cppmeth, false); if (fptr) return fptr; @@ -2712,7 +2668,7 @@ static void* PyFunction_AsCPointer(PyObject* pyobject, void* wpraddress = nullptr; // re-use existing wrapper if possible - auto key = rettype+signature; + auto key = rettype+true_signature; const auto& lookup = sWrapperLookup.find(key); if (lookup != sWrapperLookup.end()) { const auto& existing = lookup->second.find(pyobject); @@ -2740,7 +2696,7 @@ static void* PyFunction_AsCPointer(PyObject* pyobject, return nullptr; // extract argument types - const std::vector& argtypes = TypeManip::extract_arg_types(signature); + const std::vector& argtypes = TypeManip::extract_arg_types(true_signature); int nArgs = (int)argtypes.size(); // wrapper name @@ -2784,8 +2740,8 @@ static void* PyFunction_AsCPointer(PyObject* pyobject, // TODO: is there no easier way? static Cppyy::TCppScope_t scope = Cppyy::GetScope("__cppyy_internal"); - const auto& idx = Cppyy::GetMethodIndicesFromName(scope, wname.str()); - wpraddress = Cppyy::GetFunctionAddress(Cppyy::GetMethod(scope, idx[0]), false); + const auto& methods = Cppyy::GetMethodsFromName(scope, wname.str()); + wpraddress = Cppyy::GetFunctionAddress(methods[0], false); sWrapperReference[wpraddress] = ref; // cache the new wrapper @@ -2920,9 +2876,9 @@ bool CPyCppyy::SmartPtrConverter::SetArg( // for the case where we have a 'hidden' smart pointer: if (Cppyy::TCppType_t tsmart = pyobj->GetSmartIsA()) { - if (Cppyy::IsSubtype(tsmart, fSmartPtrType)) { + if (Cppyy::IsSubclass(tsmart, fSmartPtrType)) { // depending on memory policy, some objects need releasing when passed into functions - if (!fKeepControl && !UseStrictOwnership()) + if (!fKeepControl && !UseStrictOwnership(ctxt)) ((CPPInstance*)pyobject)->CppOwns(); // calculate offset between formal and actual arguments @@ -2939,7 +2895,7 @@ bool CPyCppyy::SmartPtrConverter::SetArg( } // for the case where we have an 'exposed' smart pointer: - if (!pyobj->IsSmart() && Cppyy::IsSubtype(oisa, fSmartPtrType)) { + if (!pyobj->IsSmart() && Cppyy::IsSubclass(oisa, fSmartPtrType)) { // calculate offset between formal and actual arguments para.fValue.fVoidp = pyobj->GetObject(); if (oisa != fSmartPtrType) { @@ -2953,7 +2909,7 @@ bool CPyCppyy::SmartPtrConverter::SetArg( } // for the case where we have an ordinary object to convert - if ((ctxt->fFlags & CallContext::kImplicitSmartPtrConversion) && !pyobj->IsSmart() && Cppyy::IsSubtype(oisa, fUnderlyingType)) { + if (!pyobj->IsSmart() && Cppyy::IsSubclass(oisa, fUnderlyingType)) { // create the relevant smart pointer and make the pyobject "smart" CPPInstance* pysmart = (CPPInstance*)ConvertImplicit(fSmartPtrType, pyobject, para, ctxt, false); if (!CPPInstance_Check(pysmart)) { @@ -2972,7 +2928,7 @@ bool CPyCppyy::SmartPtrConverter::SetArg( } // final option, try mapping pointer types held (TODO: do not allow for non-const ref) - if (pyobj->IsSmart() && Cppyy::IsSubtype(oisa, fUnderlyingType)) { + if (pyobj->IsSmart() && Cppyy::IsSubclass(oisa, fUnderlyingType)) { para.fValue.fVoidp = ((CPPInstance*)pyobject)->GetSmartObject(); para.fTypeCode = 'V'; return true; @@ -3039,7 +2995,7 @@ CPyCppyy::InitializerListConverter::InitializerListConverter(Cppyy::TCppType_t k : InstanceConverter{klass}, fValueTypeName{value_type}, fValueType{Cppyy::GetScope(value_type)}, - fValueSize{Cppyy::SizeOf(value_type)} + fValueSize{Cppyy::SizeOfType(Cppyy::GetType(value_type, true))} { } @@ -3127,9 +3083,8 @@ bool CPyCppyy::InitializerListConverter::SetArg( PyObject* item = PySequence_GetItem(pyobject, i); bool convert_ok = false; if (item) { - if (fConverters.empty()) - fConverters.emplace_back(CreateConverter(fValueTypeName)); - if (!fConverters.back()) { + Converter *converter = CreateConverter(fValueTypeName); + if (!converter) { if (CPPInstance_Check(item)) { // by convention, use byte copy memcpy((char*)fake->_M_array + i*fValueSize, @@ -3150,11 +3105,9 @@ bool CPyCppyy::InitializerListConverter::SetArg( } } if (memloc) { - if (i >= fConverters.size()) { - fConverters.emplace_back(CreateConverter(fValueTypeName)); - } - convert_ok = fConverters[i]->ToMemory(item, memloc); + convert_ok = converter->ToMemory(item, memloc); } + fConverters.emplace_back(converter); } @@ -3304,7 +3257,7 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim } //-- special case: initializer list - if (realType.compare(0, 16, "initializer_list") == 0) { + if (realType.compare(0, 21, "std::initializer_list") == 0) { // get the type of the list and create a converter (TODO: get hold of value_type?) auto pos = realType.find('<'); std::string value_type = realType.substr(pos+1, realType.size()-pos-2); @@ -3315,9 +3268,8 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim bool control = cpd == "&" || isConst; //-- special case: std::function - auto pos = resolvedType.find("function<"); - if (pos == 0 /* no std:: */ || pos == 5 /* with std:: */ || - pos == 6 /* const no std:: */ || pos == 11 /* const with std:: */ ) { + auto pos = resolvedType.find("std::function<"); + if (pos == 0 /* std:: */ || pos == 6 /* const std:: */ ) { // get actual converter for normal passing Converter* cnv = selectInstanceCnv( @@ -3325,14 +3277,14 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim if (cnv) { // get the type of the underlying (TODO: use target_type?) - auto pos1 = resolvedType.find("(", pos+9); + auto pos1 = resolvedType.find("(", pos+14); auto pos2 = resolvedType.rfind(")"); if (pos1 != std::string::npos && pos2 != std::string::npos) { - auto sz1 = pos1-pos-9; - if (resolvedType[pos+9+sz1-1] == ' ') sz1 -= 1; + auto sz1 = pos1-pos-14; + if (resolvedType[pos+14+sz1-1] == ' ') sz1 -= 1; return new StdFunctionConverter(cnv, - resolvedType.substr(pos+9, sz1), resolvedType.substr(pos1, pos2-pos1+1)); + resolvedType.substr(pos+14, sz1), resolvedType.substr(pos1, pos2-pos1+1)); } else if (cnv->HasState()) delete cnv; } @@ -3340,7 +3292,7 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim // converters for known C++ classes and default (void*) Converter* result = nullptr; - if (Cppyy::TCppScope_t klass = Cppyy::GetScope(realType)) { + if (Cppyy::TCppScope_t klass = Cppyy::GetFullScope(realType)) { Cppyy::TCppType_t raw{0}; if (Cppyy::GetSmartPtrInfo(realType, &raw, nullptr)) { if (cpd == "") { @@ -3371,6 +3323,7 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim resolvedType.substr(0, pos1), resolvedType.substr(pos1+sm.length(), pos2-1)); } } + const std::string failure_msg("Failed to convert type: " + fullType + "; resolved: " + resolvedType + "; real: " + realType + "; cpd: " + cpd); if (!result && cpd == "&&") { // for builtin, can use const-ref for r-ref @@ -3378,7 +3331,7 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim if (h != gConvFactories.end()) return (h->second)(dims); // else, unhandled moves - result = new NotImplementedConverter(); + result = new NotImplementedConverter(failure_msg); } if (!result && h != gConvFactories.end()) @@ -3387,11 +3340,219 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim else if (!result) { // default to something reasonable, assuming "user knows best" if (cpd.size() == 2 && cpd != "&&") // "**", "*[]", "*&" - result = new VoidPtrPtrConverter(dims.ndim()); + result = new VoidPtrPtrConverter(dims.ndim(), failure_msg); else if (!cpd.empty()) - result = new VoidArrayConverter(); // "user knows best" + result = new VoidArrayConverter(/* keepControl= */ true, failure_msg); // "user knows best" else - result = new NotImplementedConverter(); // fails on use + result = new NotImplementedConverter(failure_msg); // fails on use + } + + return result; +} + +CPYCPPYY_EXPORT +CPyCppyy::Converter* CPyCppyy::CreateConverter(Cppyy::TCppType_t type, cdims_t dims) +{ +// The matching of the fulltype to a converter factory goes through up to five levels: +// 1) full, exact match +// 2) match of decorated, unqualified type +// 3) accept const ref as by value +// 4) accept ref as pointer +// 5) generalized cases (covers basically all C++ classes) +// +// If all fails, void is used, which will generate a run-time warning when used. + +// an exactly matching converter is best + std::string fullType = Cppyy::GetTypeAsString(type); + ConvFactories_t::iterator h = gConvFactories.find(fullType); + if (h != gConvFactories.end()) { + return (h->second)(dims); + } + +// resolve typedefs etc. + Cppyy::TCppType_t resolvedType = Cppyy::ResolveType(type); + const std::string& resolvedTypeStr = Cppyy::GetTypeAsString(resolvedType); + +// a full, qualified matching converter is preferred + if (resolvedTypeStr != fullType) { + h = gConvFactories.find(resolvedTypeStr); + if (h != gConvFactories.end()) { + return (h->second)(dims); + } + } + +//-- nothing? ok, collect information about the type and possible qualifiers/decorators + bool isConst = strncmp(resolvedTypeStr.c_str(), "const", 5) == 0; + const std::string& cpd = TypeManip::compound(resolvedTypeStr); + Cppyy::TCppType_t realType = Cppyy::ResolveType(Cppyy::GetRealType(type)); + std::string realTypeStr = Cppyy::GetTypeAsString(realType); + std::string realUnresolvedTypeStr = TypeManip::clean_type(fullType, false, true); + +// accept unqualified type (as python does not know about qualifiers) + h = gConvFactories.find((isConst ? "const " : "") + realTypeStr + cpd); + if (h != gConvFactories.end()) { + return (h->second)(dims); + } + +// drop const, as that is mostly meaningless to python (with the exception +// of c-strings, but those are specialized in the converter map) + if (isConst) { + h = gConvFactories.find(realTypeStr + cpd); + if (h != gConvFactories.end()) { + return (h->second)(dims); + } + } + +//-- still nothing? try pointer instead of array (for builtins) + if (cpd.compare(0, 3, "*[]") == 0) { + // special case, array of pointers + h = gConvFactories.find(realTypeStr + " ptr"); + if (h != gConvFactories.end()) { + // upstream treats the pointer type as the array element type, but that pointer is + // treated as a low-level view as well, unless it's a void*/char* so adjust the dims + if (realTypeStr != "void" && realTypeStr != "char") { + dim_t newdim = dims.ndim() == UNKNOWN_SIZE ? 2 : dims.ndim()+1; + dims_t newdims = dims_t(newdim); + // TODO: sometimes the array size is known and can thus be verified; however, + // currently the meta layer does not provide this information + newdims[0] = dims ? dims[0] : UNKNOWN_SIZE; // the array + newdims[1] = UNKNOWN_SIZE; // the pointer + if (2 < newdim) { + for (int i = 2; i < (newdim-1); ++i) + newdims[i] = dims[i-1]; + } + + return (h->second)(newdims); + } + return (h->second)(dims); + } + + } else if (!cpd.empty() && (std::string::size_type)std::count(cpd.begin(), cpd.end(), '*') == cpd.size()) { + // simple array; set or resize as necessary + h = gConvFactories.find(realTypeStr + " ptr"); + if (h != gConvFactories.end()) + return (h->second)((!dims && 1 < cpd.size()) ? dims_t(cpd.size()) : dims); + + } else if (2 <= cpd.size() && (std::string::size_type)std::count(cpd.begin(), cpd.end(), '[') == cpd.size() / 2) { + // fixed array, dims will have size if available + h = gConvFactories.find(realTypeStr + " ptr"); + if (h != gConvFactories.end()) + return (h->second)(dims); + } + +//-- special case: initializer list + if (realTypeStr.compare(0, 21, "std::initializer_list") == 0) { + // get the type of the list and create a converter (TODO: get hold of value_type?) + auto pos = realTypeStr.find('<'); + std::string value_type = realTypeStr.substr(pos+1, realTypeStr.size()-pos-2); + Converter* cnv = nullptr; bool use_byte_cnv = false; + if (cpd == "" && Cppyy::GetScope(value_type)) { + // initializer list of object values does not work as the target is raw + // memory; simply use byte copies + + // by convention, leave cnv as nullptr + use_byte_cnv = true; + } else + cnv = CreateConverter(value_type); + if (cnv || use_byte_cnv) + return new InitializerListConverter(Cppyy::GetScopeFromType(realType), value_type); + } + +//-- still nothing? use a generalized converter + bool control = cpd == "&" || isConst; + +//-- special case: std::function + auto pos = resolvedTypeStr.find("std::function<"); + if (pos == 0 /* std:: */ || pos == 6 /* const std:: */ ) { + + // get actual converter for normal passing + Converter* cnv = selectInstanceCnv( + Cppyy::GetScopeFromType(realType), cpd, dims, isConst, control); + + if (cnv) { + // get the type of the underlying (TODO: use target_type?) + auto pos1 = resolvedTypeStr.find("(", pos+14); + auto pos2 = resolvedTypeStr.rfind(")"); + if (pos1 != std::string::npos && pos2 != std::string::npos) { + auto sz1 = pos1-pos-14; + if (resolvedTypeStr[pos+14+sz1-1] == ' ') sz1 -= 1; + + const std::string &argsStr = resolvedTypeStr.substr(pos1, pos2-pos1+1).c_str(); + return new StdFunctionConverter(cnv, + resolvedTypeStr.substr(pos+14, sz1), argsStr == "(void)"? "()" : argsStr); + } else if (cnv->HasState()) + delete cnv; + } + } + +// converters for known C++ classes and default (void*) + Converter* result = nullptr; + Cppyy::TCppScope_t klass = Cppyy::GetScopeFromType(realType); + if (resolvedTypeStr.find("(*)") != std::string::npos || + (resolvedTypeStr.find("::*)") != std::string::npos)) { + // this is a function function pointer + // TODO: find better way of finding the type + auto pos1 = resolvedTypeStr.find('('); + auto pos2 = resolvedTypeStr.find("*)"); + auto pos3 = resolvedTypeStr.rfind(')'); + std::string return_type = resolvedTypeStr.substr(0, pos1); + result = new FunctionPointerConverter( + return_type.erase(return_type.find_last_not_of(" ") + 1), resolvedTypeStr.substr(pos2+2, pos3-pos2-1)); + } else if ((realTypeStr != "std::byte") && (klass || (klass = Cppyy::GetFullScope(realTypeStr)))) { + // std::byte is a special enum class used to access raw memory + Cppyy::TCppType_t raw{0}; + if (Cppyy::GetSmartPtrInfo(realTypeStr, &raw, nullptr)) { + if (cpd == "") { + result = new SmartPtrConverter(klass, raw, control); + } else if (cpd == "&") { + result = new SmartPtrConverter(klass, raw); + } else if (cpd == "*" && dims.ndim() == UNKNOWN_SIZE) { + result = new SmartPtrConverter(klass, raw, control, true); + } + } + + if (!result) { + // CLING WORKAROUND -- special case for STL iterators + if (realTypeStr.rfind("__gnu_cxx::__normal_iterator", 0) /* vector */ == 0 +#ifdef __APPLE__ + || realTypeStr.rfind("__wrap_iter", 0) == 0 +#endif + // TODO: Windows? + ) { + static STLIteratorConverter c; + result = &c; + } else { + // -- CLING WORKAROUND + result = selectInstanceCnv(klass, cpd, dims, isConst, control); + } + } + } + const std::string failure_msg("Failed to convert type: " + fullType + "; resolved: " + resolvedTypeStr + "; real: " + realTypeStr + "; realUnresolvedType: " + realUnresolvedTypeStr + "; cpd: " + cpd); + + if (!result && cpd == "&&") { + // for builtin, can use const-ref for r-ref + h = gConvFactories.find("const " + realTypeStr + "&"); + if (h != gConvFactories.end()) + return (h->second)(dims); + h = gConvFactories.find("const " + realUnresolvedTypeStr + "&"); + if (h != gConvFactories.end()) + return (h->second)(dims); + // else, unhandled moves + result = new NotImplementedConverter(failure_msg); + } + + if (!result && h != gConvFactories.end()) { + // converter factory available, use it to create converter + result = (h->second)(dims); + } else if (!result) { + // default to something reasonable, assuming "user knows best" + if (cpd.size() == 2 && cpd != "&&") {// "**", "*[]", "*&" + result = new VoidPtrPtrConverter(dims.ndim(), failure_msg); + } else if (!cpd.empty()) { + result = new VoidArrayConverter(/* keepControl= */ true, failure_msg); // "user knows best" + } else { + result = new NotImplementedConverter(failure_msg); // fails on use + } } return result; @@ -3459,7 +3620,7 @@ std::string::size_type dims2stringsz(cdims_t d) { return (d && d.ndim() != UNKNOWN_SIZE) ? d[0] : std::string::npos; } -#define STRINGVIEW "basic_string_view >" +#define STRINGVIEW "std::basic_string_view" #define WSTRING1 "std::basic_string" #define WSTRING2 "std::basic_string,std::allocator>" @@ -3500,21 +3661,9 @@ static struct InitConvFactories_t { gf["int8_t"] = (cf_t)+[](cdims_t) { static Int8Converter c{}; return &c; }; gf["const int8_t&"] = (cf_t)+[](cdims_t) { static ConstInt8RefConverter c{}; return &c; }; gf["int8_t&"] = (cf_t)+[](cdims_t) { static Int8RefConverter c{}; return &c; }; - gf["int16_t"] = (cf_t)+[](cdims_t) { static Int16Converter c{}; return &c; }; - gf["const int16_t&"] = (cf_t)+[](cdims_t) { static ConstInt16RefConverter c{}; return &c; }; - gf["int16_t&"] = (cf_t)+[](cdims_t) { static Int16RefConverter c{}; return &c; }; - gf["int32_t"] = (cf_t)+[](cdims_t) { static Int32Converter c{}; return &c; }; - gf["const int32_t&"] = (cf_t)+[](cdims_t) { static ConstInt32RefConverter c{}; return &c; }; - gf["int32_t&"] = (cf_t)+[](cdims_t) { static Int32RefConverter c{}; return &c; }; gf["uint8_t"] = (cf_t)+[](cdims_t) { static UInt8Converter c{}; return &c; }; gf["const uint8_t&"] = (cf_t)+[](cdims_t) { static ConstUInt8RefConverter c{}; return &c; }; gf["uint8_t&"] = (cf_t)+[](cdims_t) { static UInt8RefConverter c{}; return &c; }; - gf["uint16_t"] = (cf_t)+[](cdims_t) { static UInt16Converter c{}; return &c; }; - gf["const uint16_t&"] = (cf_t)+[](cdims_t) { static ConstUInt16RefConverter c{}; return &c; }; - gf["uint16_t&"] = (cf_t)+[](cdims_t) { static UInt16RefConverter c{}; return &c; }; - gf["uint32_t"] = (cf_t)+[](cdims_t) { static UInt32Converter c{}; return &c; }; - gf["const uint32_t&"] = (cf_t)+[](cdims_t) { static ConstUInt32RefConverter c{}; return &c; }; - gf["uint32_t&"] = (cf_t)+[](cdims_t) { static UInt32RefConverter c{}; return &c; }; gf["short"] = (cf_t)+[](cdims_t) { static ShortConverter c{}; return &c; }; gf["const short&"] = (cf_t)+[](cdims_t) { static ConstShortRefConverter c{}; return &c; }; gf["short&"] = (cf_t)+[](cdims_t) { static ShortRefConverter c{}; return &c; }; @@ -3567,11 +3716,7 @@ static struct InitConvFactories_t { gf["std::byte ptr"] = (cf_t)+[](cdims_t d) { return new ByteArrayConverter{d}; }; #endif gf["int8_t ptr"] = (cf_t)+[](cdims_t d) { return new Int8ArrayConverter{d}; }; - gf["int16_t ptr"] = (cf_t)+[](cdims_t d) { return new Int16ArrayConverter{d}; }; - gf["int32_t ptr"] = (cf_t)+[](cdims_t d) { return new Int32ArrayConverter{d}; }; gf["uint8_t ptr"] = (cf_t)+[](cdims_t d) { return new UInt8ArrayConverter{d}; }; - gf["uint16_t ptr"] = (cf_t)+[](cdims_t d) { return new UInt16ArrayConverter{d}; }; - gf["uint32_t ptr"] = (cf_t)+[](cdims_t d) { return new UInt32ArrayConverter{d}; }; gf["short ptr"] = (cf_t)+[](cdims_t d) { return new ShortArrayConverter{d}; }; gf["unsigned short ptr"] = (cf_t)+[](cdims_t d) { return new UShortArrayConverter{d}; }; gf["int ptr"] = (cf_t)+[](cdims_t d) { return new IntArrayConverter{d}; }; @@ -3592,11 +3737,8 @@ static struct InitConvFactories_t { gf["const signed char&"] = gf["const char&"]; #if (__cplusplus > 201402L) || (defined(_MSC_VER) && _MSVC_LANG > 201402L) gf["std::byte"] = gf["uint8_t"]; - gf["byte"] = gf["uint8_t"]; gf["const std::byte&"] = gf["const uint8_t&"]; - gf["const byte&"] = gf["const uint8_t&"]; gf["std::byte&"] = gf["uint8_t&"]; - gf["byte&"] = gf["uint8_t&"]; #endif gf["std::int8_t"] = gf["int8_t"]; gf["const std::int8_t&"] = gf["const int8_t&"]; @@ -3645,18 +3787,18 @@ static struct InitConvFactories_t { gf["const char*[]"] = (cf_t)+[](cdims_t d) { return new CStringArrayConverter{d, false}; }; gf["char*[]"] = (cf_t)+[](cdims_t d) { return new NonConstCStringArrayConverter{d, false}; }; gf["char ptr"] = gf["char*[]"]; - gf["std::string"] = (cf_t)+[](cdims_t) { return new STLStringConverter{}; }; - gf["const std::string&"] = gf["std::string"]; - gf["string"] = gf["std::string"]; - gf["const string&"] = gf["std::string"]; - gf["std::string&&"] = (cf_t)+[](cdims_t) { return new STLStringMoveConverter{}; }; - gf["string&&"] = gf["std::string&&"]; + gf["std::basic_string"] = (cf_t)+[](cdims_t) { return new STLStringConverter{}; }; + gf["const std::basic_string&"] = gf["std::basic_string"]; + gf["std::basic_string&&"] = (cf_t)+[](cdims_t) { return new STLStringMoveConverter{}; }; + gf["const std::basic_string &"] = gf["std::basic_string"]; + gf["std::basic_string &&"] = (cf_t)+[](cdims_t) { return new STLStringMoveConverter{}; }; #if (__cplusplus > 201402L) || (defined(_MSC_VER) && _MSVC_LANG > 201402L) - gf["std::string_view"] = (cf_t)+[](cdims_t) { return new STLStringViewConverter{}; }; - gf[STRINGVIEW] = gf["std::string_view"]; - gf["std::string_view&"] = gf["std::string_view"]; - gf["const std::string_view&"] = gf["std::string_view"]; - gf["const " STRINGVIEW "&"] = gf["std::string_view"]; + gf["std::basic_string_view"] = (cf_t)+[](cdims_t) { return new STLStringViewConverter{}; }; + gf[STRINGVIEW] = gf["std::basic_string_view"]; + gf["std::basic_string_view&"] = gf["std::basic_string_view"]; + gf["const " STRINGVIEW "&"] = gf["std::basic_string_view"]; + gf["std::basic_string_view &"] = gf["std::basic_string_view"]; + gf["const " STRINGVIEW " &"] = gf["std::basic_string_view"]; #endif gf["std::wstring"] = (cf_t)+[](cdims_t) { return new STLWStringConverter{}; }; gf[WSTRING1] = gf["std::wstring"]; @@ -3664,6 +3806,9 @@ static struct InitConvFactories_t { gf["const std::wstring&"] = gf["std::wstring"]; gf["const " WSTRING1 "&"] = gf["std::wstring"]; gf["const " WSTRING2 "&"] = gf["std::wstring"]; + gf["const std::wstring &"] = gf["std::wstring"]; + gf["const " WSTRING1 " &"] = gf["std::wstring"]; + gf["const " WSTRING2 " &"] = gf["std::wstring"]; gf["void*&"] = (cf_t)+[](cdims_t) { static VoidPtrRefConverter c{}; return &c; }; gf["void**"] = (cf_t)+[](cdims_t d) { return new VoidPtrPtrConverter{d}; }; gf["void ptr"] = gf["void**"]; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Converters.h b/bindings/pyroot/cppyy/CPyCppyy/src/Converters.h index 052e1f2ae1a4d..92cccbacb87a9 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Converters.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Converters.h @@ -29,10 +29,12 @@ class CPYCPPYY_CLASS_EXPORT Converter { virtual PyObject* FromMemory(void* address); virtual bool ToMemory(PyObject* value, void* address, PyObject* ctxt = nullptr); virtual bool HasState() { return false; } + virtual std::string GetFailureMsg() { return "[Converter]"; } }; // create/destroy converter from fully qualified type (public API) CPYCPPYY_EXPORT Converter* CreateConverter(const std::string& fullType, cdims_t dims = 0); +CPYCPPYY_EXPORT Converter* CreateConverter(Cppyy::TCppType_t type, cdims_t dims = 0); CPYCPPYY_EXPORT void DestroyConverter(Converter* p); typedef Converter* (*cf_t)(cdims_t d); CPYCPPYY_EXPORT bool RegisterConverter(const std::string& name, cf_t fac); @@ -43,17 +45,20 @@ CPYCPPYY_EXPORT bool UnregisterConverter(const std::string& name); // converters for special cases (only here b/c of external use of StrictInstancePtrConverter) class VoidArrayConverter : public Converter { public: - VoidArrayConverter(bool keepControl = true) { fKeepControl = keepControl; } + VoidArrayConverter(bool keepControl = true, const std::string &failureMsg = std::string()) + : fFailureMsg(failureMsg) { fKeepControl = keepControl; } public: bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; PyObject* FromMemory(void* address) override; bool ToMemory(PyObject* value, void* address, PyObject* ctxt = nullptr) override; bool HasState() override { return true; } + virtual std::string GetFailureMsg() { return "[VoidArrayConverter] " + fFailureMsg; } protected: virtual bool GetAddressSpecialCase(PyObject* pyobject, void*& address); bool KeepControl() { return fKeepControl; } + const std::string fFailureMsg; private: bool fKeepControl; @@ -62,8 +67,8 @@ class VoidArrayConverter : public Converter { template class InstancePtrConverter : public VoidArrayConverter { public: - InstancePtrConverter(Cppyy::TCppType_t klass, bool keepControl = false) : - VoidArrayConverter(keepControl), fClass(klass) {} + InstancePtrConverter(Cppyy::TCppScope_t klass, bool keepControl = false, const std::string &failureMsg = std::string()) : + VoidArrayConverter(keepControl, failureMsg), fClass(Cppyy::GetUnderlyingScope(klass)) {} public: bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Cppyy.h b/bindings/pyroot/cppyy/CPyCppyy/src/Cppyy.h index cc1340033bbaf..2212ff3330c03 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Cppyy.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Cppyy.h @@ -9,7 +9,11 @@ #include // import/export (after precommondefs.h from PyPy) +#ifdef _MSC_VER +#define CPPYY_IMPORT extern __declspec(dllimport) +#else #define CPPYY_IMPORT extern +#endif // some more types; assumes Cppyy.h follows Python.h #ifndef PY_LONG_LONG @@ -32,14 +36,22 @@ typedef unsigned long long PY_ULONG_LONG; typedef long double PY_LONG_DOUBLE; #endif +namespace Cpp { + struct TemplateArgInfo { + void* m_Type; + const char* m_IntegralValue; + TemplateArgInfo(void* type, const char* integral_value = nullptr) + : m_Type(type), m_IntegralValue(integral_value) {} + }; +} // end namespace Cpp namespace Cppyy { - typedef size_t TCppScope_t; + typedef void* TCppScope_t; typedef TCppScope_t TCppType_t; typedef void* TCppEnum_t; typedef void* TCppObject_t; - typedef intptr_t TCppMethod_t; + typedef void* TCppMethod_t; typedef size_t TCppIndex_t; typedef void* TCppFuncAddr_t; @@ -53,21 +65,57 @@ namespace Cppyy { // name to opaque C++ scope representation ----------------------------------- CPPYY_IMPORT std::string ResolveName(const std::string& cppitem_name); + + CPPYY_IMPORT + TCppType_t ResolveEnumReferenceType(TCppType_t type); + CPPYY_IMPORT + TCppType_t ResolveEnumPointerType(TCppType_t type); + + CPPYY_IMPORT + TCppType_t ResolveType(TCppType_t type); + CPPYY_IMPORT + TCppType_t GetRealType(TCppType_t type); + CPPYY_IMPORT + TCppType_t GetReferencedType(TCppType_t type, bool rvalue); + CPPYY_IMPORT + TCppType_t GetPointerType(TCppType_t type); + CPPYY_IMPORT + std::string ResolveEnum(TCppScope_t enum_type); + CPPYY_IMPORT + TCppScope_t GetScope(const std::string& name, TCppScope_t parent_scope = 0); + CPPYY_IMPORT + TCppScope_t GetUnderlyingScope(TCppScope_t scope); CPPYY_IMPORT - std::string ResolveEnum(const std::string& enum_type); + TCppScope_t GetFullScope(const std::string& scope_name); CPPYY_IMPORT - TCppScope_t GetScope(const std::string& scope_name); + TCppScope_t GetTypeScope(TCppScope_t klass); CPPYY_IMPORT - TCppType_t GetActualClass(TCppType_t klass, TCppObject_t obj); + TCppScope_t GetNamed(const std::string& scope_name, + TCppScope_t parent_scope = 0); CPPYY_IMPORT - size_t SizeOf(TCppType_t klass); + TCppScope_t GetParentScope(TCppScope_t scope); + CPPYY_IMPORT + TCppScope_t GetScopeFromType(TCppScope_t type); + CPPYY_IMPORT + TCppType_t GetTypeFromScope(TCppScope_t klass); + CPPYY_IMPORT + TCppScope_t GetGlobalScope(); + CPPYY_IMPORT + TCppScope_t GetActualClass(TCppScope_t klass, TCppObject_t obj); + CPPYY_IMPORT + size_t SizeOf(TCppScope_t klass); + CPPYY_IMPORT + size_t SizeOfType(TCppType_t type); CPPYY_IMPORT size_t SizeOf(const std::string& type_name); CPPYY_IMPORT bool IsBuiltin(const std::string& type_name); CPPYY_IMPORT - bool IsComplete(const std::string& type_name); + bool IsComplete(TCppScope_t scope); + + CPPYY_IMPORT + bool IsPointerType(TCppType_t type); CPPYY_IMPORT TCppScope_t gGlobalScope; // for fast access @@ -108,7 +156,7 @@ namespace Cppyy { CPPYY_IMPORT char* CallS(TCppMethod_t method, TCppObject_t self, size_t nargs, void* args, size_t* length); CPPYY_IMPORT - TCppObject_t CallConstructor(TCppMethod_t method, TCppType_t type, size_t nargs, void* args); + TCppObject_t CallConstructor(TCppMethod_t method, TCppScope_t klass, size_t nargs, void* args); CPPYY_IMPORT void CallDestructor(TCppType_t type, TCppObject_t self); CPPYY_IMPORT @@ -131,15 +179,27 @@ namespace Cppyy { CPPYY_IMPORT bool IsNamespace(TCppScope_t scope); CPPYY_IMPORT - bool IsTemplate(const std::string& template_name); + bool IsClass(TCppScope_t scope); + CPPYY_IMPORT + bool IsTemplate(TCppScope_t handle); + CPPYY_IMPORT + bool IsTemplateInstantiation(TCppScope_t handle); + CPPYY_IMPORT + bool IsTypedefed(TCppScope_t handle); CPPYY_IMPORT bool IsAbstract(TCppType_t type); CPPYY_IMPORT - bool IsEnum(const std::string& type_name); + bool IsEnumScope(TCppScope_t scope); + CPPYY_IMPORT + bool IsEnumConstant(TCppScope_t scope); + CPPYY_IMPORT + bool IsEnumType(TCppType_t type); CPPYY_IMPORT bool IsAggregate(TCppType_t type); CPPYY_IMPORT - bool IsDefaultConstructable(TCppType_t type); + bool IsDefaultConstructable(TCppScope_t scope); + CPPYY_IMPORT + bool IsVariable(TCppScope_t scope); CPPYY_IMPORT void GetAllCppNames(TCppScope_t scope, std::set& cppnames); @@ -164,7 +224,9 @@ namespace Cppyy { CPPYY_IMPORT std::string GetBaseName(TCppType_t type, TCppIndex_t ibase); CPPYY_IMPORT - bool IsSubtype(TCppType_t derived, TCppType_t base); + TCppScope_t GetBaseScope(TCppType_t type, TCppIndex_t ibase); + CPPYY_IMPORT + bool IsSubclass(TCppType_t derived, TCppType_t base); CPPYY_IMPORT bool IsSmartPtr(TCppType_t type); CPPYY_IMPORT @@ -182,9 +244,9 @@ namespace Cppyy { // method/function reflection information ------------------------------------ CPPYY_IMPORT - TCppIndex_t GetNumMethods(TCppScope_t scope, bool accept_namespace = false); + void GetClassMethods(TCppScope_t scope, std::vector &methods); CPPYY_IMPORT - std::vector GetMethodIndicesFromName(TCppScope_t scope, const std::string& name); + std::vector GetMethodsFromName(TCppScope_t scope, const std::string& name); CPPYY_IMPORT TCppMethod_t GetMethod(TCppScope_t scope, TCppIndex_t imeth); @@ -196,7 +258,9 @@ namespace Cppyy { CPPYY_IMPORT std::string GetMethodMangledName(TCppMethod_t); CPPYY_IMPORT - std::string GetMethodResultType(TCppMethod_t); + TCppType_t GetMethodReturnType(TCppMethod_t); + CPPYY_IMPORT + std::string GetMethodReturnTypeAsString(TCppMethod_t); CPPYY_IMPORT TCppIndex_t GetMethodNumArgs(TCppMethod_t); CPPYY_IMPORT @@ -204,39 +268,45 @@ namespace Cppyy { CPPYY_IMPORT std::string GetMethodArgName(TCppMethod_t, TCppIndex_t iarg); CPPYY_IMPORT - std::string GetMethodArgType(TCppMethod_t, TCppIndex_t iarg); + TCppType_t GetMethodArgType(TCppMethod_t, TCppIndex_t iarg); + CPPYY_IMPORT + std::string GetMethodArgTypeAsString(TCppMethod_t, TCppIndex_t iarg); + CPPYY_IMPORT + std::string GetMethodArgCanonTypeAsString(TCppMethod_t, TCppIndex_t iarg); CPPYY_IMPORT TCppIndex_t CompareMethodArgType(TCppMethod_t, TCppIndex_t iarg, const std::string &req_type); CPPYY_IMPORT std::string GetMethodArgDefault(TCppMethod_t, TCppIndex_t iarg); CPPYY_IMPORT - std::string GetMethodSignature(TCppMethod_t, bool show_formalargs, TCppIndex_t maxargs = (TCppIndex_t)-1); + std::string GetMethodSignature(TCppMethod_t, bool show_formal_args, TCppIndex_t max_args = (TCppIndex_t)-1); CPPYY_IMPORT - std::string GetMethodPrototype(TCppScope_t scope, TCppMethod_t, bool show_formalargs); + std::string GetMethodPrototype(TCppMethod_t, bool show_formal_args); CPPYY_IMPORT bool IsConstMethod(TCppMethod_t); - + CPPYY_IMPORT + void GetTemplatedMethods(TCppScope_t scope, std::vector &methods); CPPYY_IMPORT TCppIndex_t GetNumTemplatedMethods(TCppScope_t scope, bool accept_namespace = false); CPPYY_IMPORT std::string GetTemplatedMethodName(TCppScope_t scope, TCppIndex_t imeth); CPPYY_IMPORT - bool IsTemplatedConstructor(TCppScope_t scope, TCppIndex_t imeth); - CPPYY_IMPORT bool ExistsMethodTemplate(TCppScope_t scope, const std::string& name); CPPYY_IMPORT - bool IsStaticTemplate(TCppScope_t scope, const std::string& name); + bool IsTemplatedMethod(TCppMethod_t method); CPPYY_IMPORT - bool IsMethodTemplate(TCppScope_t scope, TCppIndex_t imeth); + bool IsStaticTemplate(TCppScope_t scope, const std::string& name); CPPYY_IMPORT TCppMethod_t GetMethodTemplate( TCppScope_t scope, const std::string& name, const std::string& proto); CPPYY_IMPORT - TCppIndex_t GetGlobalOperator( - TCppType_t scope, const std::string& lc, const std::string& rc, const std::string& op); + TCppMethod_t GetGlobalOperator(TCppType_t scope, const std::string &lc, + const std::string &rc, + const std::string &op); // method properties --------------------------------------------------------- + CPPYY_IMPORT + bool IsDeletedMethod(TCppMethod_t method); CPPYY_IMPORT bool IsPublicMethod(TCppMethod_t method); CPPYY_IMPORT @@ -247,45 +317,73 @@ namespace Cppyy { bool IsDestructor(TCppMethod_t method); CPPYY_IMPORT bool IsStaticMethod(TCppMethod_t method); - CPPYY_IMPORT - bool IsExplicit(TCppMethod_t method); // data member reflection information ---------------------------------------- CPPYY_IMPORT - TCppIndex_t GetNumDatamembers(TCppScope_t scope, bool accept_namespace = false); + void GetDatamembers(TCppScope_t scope, std::vector& datamembers); CPPYY_IMPORT std::string GetDatamemberName(TCppScope_t scope, TCppIndex_t idata); CPPYY_IMPORT - std::string GetDatamemberType(TCppScope_t scope, TCppIndex_t idata); + TCppScope_t ReduceReturnType(TCppScope_t fn, TCppType_t reduce); + bool IsLambdaClass(TCppType_t type); CPPYY_IMPORT - intptr_t GetDatamemberOffset(TCppScope_t scope, TCppIndex_t idata); + TCppScope_t WrapLambdaFromVariable(TCppScope_t var); CPPYY_IMPORT - TCppIndex_t GetDatamemberIndex(TCppScope_t scope, const std::string& name); + TCppScope_t AdaptFunctionForLambdaReturn(TCppScope_t fn); + CPPYY_IMPORT + TCppType_t GetDatamemberType(TCppScope_t var); + CPPYY_IMPORT + std::string GetTypeAsString(TCppType_t type); + CPPYY_IMPORT + bool IsClassType(TCppType_t type); + CPPYY_IMPORT + bool IsFunctionPointerType(TCppType_t type); + CPPYY_IMPORT + TCppType_t GetType(const std::string& name, bool enable_slow_lookup = false); + CPPYY_IMPORT + bool AppendTypesSlow(const std::string &name, + std::vector& types, + TCppScope_t parent = nullptr); + CPPYY_IMPORT + TCppType_t GetComplexType(const std::string& element_type); + CPPYY_IMPORT + std::string GetDatamemberTypeAsString(TCppScope_t var); + CPPYY_IMPORT + intptr_t GetDatamemberOffset(TCppScope_t var, TCppScope_t klass = nullptr); + CPPYY_IMPORT + bool CheckDatamember(TCppScope_t scope, const std::string& name); // data member properties ---------------------------------------------------- CPPYY_IMPORT - bool IsPublicData(TCppScope_t scope, TCppIndex_t idata); + bool IsPublicData(TCppScope_t data); CPPYY_IMPORT - bool IsProtectedData(TCppScope_t scope, TCppIndex_t idata); + bool IsProtectedData(TCppScope_t var); CPPYY_IMPORT - bool IsStaticData(TCppScope_t scope, TCppIndex_t idata); + bool IsStaticDatamember(TCppScope_t var); CPPYY_IMPORT - bool IsConstData(TCppScope_t scope, TCppIndex_t idata); + bool IsConstVar(TCppScope_t var); CPPYY_IMPORT bool IsEnumData(TCppScope_t scope, TCppIndex_t idata); CPPYY_IMPORT - int GetDimensionSize(TCppScope_t scope, TCppIndex_t idata, int dimension); + std::vector GetDimensions(TCppType_t type); // enum properties ----------------------------------------------------------- + // CPPYY_IMPORT + // TCppEnum_t GetEnum(TCppScope_t scope, const std::string& enum_name); + CPPYY_IMPORT + TCppScope_t GetEnumScope(TCppScope_t); CPPYY_IMPORT - TCppEnum_t GetEnum(TCppScope_t scope, const std::string& enum_name); + std::vector GetEnumConstants(TCppScope_t scope); CPPYY_IMPORT - TCppIndex_t GetNumEnumData(TCppEnum_t); + TCppType_t GetEnumConstantType(TCppScope_t scope); CPPYY_IMPORT - std::string GetEnumDataName(TCppEnum_t, TCppIndex_t idata); + long long GetEnumDataValue(TCppScope_t scope); CPPYY_IMPORT - long long GetEnumDataValue(TCppEnum_t, TCppIndex_t idata); + TCppScope_t InstantiateTemplate( + TCppScope_t tmpl, Cpp::TemplateArgInfo* args, size_t args_size); + CPPYY_IMPORT + TCppScope_t DumpScope(TCppScope_t scope); } // namespace Cppyy #endif // !CPYCPPYY_CPPYY_H diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CustomPyTypes.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CustomPyTypes.cxx index f6ec71bac8695..7c021866e25b7 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CustomPyTypes.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CustomPyTypes.cxx @@ -144,7 +144,7 @@ PyTypeObject TypedefPointerToClass_Type = { 0, // tp_dict 0, // tp_descr_get 0, // tp_descr_set - offsetof(typedefpointertoclassobject, fDict), // tp_dictoffset + 0, // tp_dictoffset 0, // tp_init 0, // tp_alloc 0, // tp_new diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CustomPyTypes.h b/bindings/pyroot/cppyy/CPyCppyy/src/CustomPyTypes.h index caa0fca3608f6..b9fbf073dca0e 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CustomPyTypes.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CustomPyTypes.h @@ -43,11 +43,6 @@ inline bool RefInt_CheckExact(T* object) struct typedefpointertoclassobject { PyObject_HEAD Cppyy::TCppType_t fCppType; - PyObject* fDict; - - ~typedefpointertoclassobject() { - Py_DECREF(fDict); - } }; extern PyTypeObject TypedefPointerToClass_Type; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/DeclareConverters.h b/bindings/pyroot/cppyy/CPyCppyy/src/DeclareConverters.h index 2f7f0a25e6920..2238de6093840 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/DeclareConverters.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/DeclareConverters.h @@ -17,36 +17,41 @@ namespace { #define CPPYY_DECLARE_BASIC_CONVERTER(name) \ class name##Converter : public Converter { \ public: \ - bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ - PyObject* FromMemory(void*) override; \ - bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; \ + bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ + PyObject* FromMemory(void*) override; \ + bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; \ + virtual std::string GetFailureMsg() { return "[" #name "Converter]"; } \ }; \ \ class Const##name##RefConverter : public Converter { \ public: \ - bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ - PyObject* FromMemory(void*) override; \ + bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ + PyObject* FromMemory(void*) override; \ + virtual std::string GetFailureMsg() { return "[Const" #name "RefConverter]"; }\ } #define CPPYY_DECLARE_BASIC_CONVERTER2(name, base) \ class name##Converter : public base##Converter { \ public: \ - PyObject* FromMemory(void*) override; \ - bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; \ + PyObject* FromMemory(void*) override; \ + bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; \ + virtual std::string GetFailureMsg() { return "[" #name "Converter]"; } \ }; \ \ class Const##name##RefConverter : public Converter { \ public: \ - bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ - PyObject* FromMemory(void*) override; \ + bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ + PyObject* FromMemory(void*) override; \ + virtual std::string GetFailureMsg() { return "[Const" #name "RefConverter]"; }\ } #define CPPYY_DECLARE_REFCONVERTER(name) \ class name##RefConverter : public Converter { \ public: \ - bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ - PyObject* FromMemory(void*) override; \ + bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ + PyObject* FromMemory(void*) override; \ + virtual std::string GetFailureMsg() { return "[" #name "RefConverter]"; }\ }; #define CPPYY_DECLARE_ARRAY_CONVERTER(name) \ @@ -55,10 +60,11 @@ public: \ name##ArrayConverter(cdims_t dims); \ name##ArrayConverter(const name##ArrayConverter&) = delete; \ name##ArrayConverter& operator=(const name##ArrayConverter&) = delete; \ - bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ - PyObject* FromMemory(void*) override; \ - bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; \ - bool HasState() override { return true; } \ + bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ + PyObject* FromMemory(void*) override; \ + bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; \ + bool HasState() { return true; } \ + virtual std::string GetFailureMsg() { return "[" #name "ArrayConverter]"; }\ protected: \ dims_t fShape; \ bool fIsFixed; \ @@ -84,11 +90,7 @@ CPPYY_DECLARE_BASIC_CONVERTER(WChar); CPPYY_DECLARE_BASIC_CONVERTER(Char16); CPPYY_DECLARE_BASIC_CONVERTER(Char32); CPPYY_DECLARE_BASIC_CONVERTER(Int8); -CPPYY_DECLARE_BASIC_CONVERTER(Int16); -CPPYY_DECLARE_BASIC_CONVERTER(Int32); CPPYY_DECLARE_BASIC_CONVERTER(UInt8); -CPPYY_DECLARE_BASIC_CONVERTER(UInt16); -CPPYY_DECLARE_BASIC_CONVERTER(UInt32); CPPYY_DECLARE_BASIC_CONVERTER(Short); CPPYY_DECLARE_BASIC_CONVERTER(UShort); CPPYY_DECLARE_BASIC_CONVERTER(Int); @@ -108,11 +110,7 @@ CPPYY_DECLARE_REFCONVERTER(Char32); CPPYY_DECLARE_REFCONVERTER(SChar); CPPYY_DECLARE_REFCONVERTER(UChar); CPPYY_DECLARE_REFCONVERTER(Int8); -CPPYY_DECLARE_REFCONVERTER(Int16); -CPPYY_DECLARE_REFCONVERTER(Int32); CPPYY_DECLARE_REFCONVERTER(UInt8); -CPPYY_DECLARE_REFCONVERTER(UInt16); -CPPYY_DECLARE_REFCONVERTER(UInt32); CPPYY_DECLARE_REFCONVERTER(Short); CPPYY_DECLARE_REFCONVERTER(UShort); CPPYY_DECLARE_REFCONVERTER(UInt); @@ -139,6 +137,7 @@ class CStringConverter : public Converter { PyObject* FromMemory(void* address) override; bool ToMemory(PyObject* value, void* address, PyObject* = nullptr) override; bool HasState() override { return true; } + virtual std::string GetFailureMsg() { return "[CStringConverter]"; } protected: std::string fBuffer; @@ -152,6 +151,7 @@ class NonConstCStringConverter : public CStringConverter { public: bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; PyObject* FromMemory(void* address) override; + virtual std::string GetFailureMsg() { return "[NonConstCStringConverter]"; } }; class WCStringConverter : public Converter { @@ -167,6 +167,7 @@ class WCStringConverter : public Converter { PyObject* FromMemory(void* address) override; bool ToMemory(PyObject* value, void* address, PyObject* = nullptr) override; bool HasState() override { return true; } + virtual std::string GetFailureMsg() { return "[WCStringConverter]"; }; protected: wchar_t* fBuffer; @@ -186,6 +187,7 @@ class CString16Converter : public Converter { PyObject* FromMemory(void* address) override; bool ToMemory(PyObject* value, void* address, PyObject* = nullptr) override; bool HasState() override { return true; } + virtual std::string GetFailureMsg() { return "[CString16Converter]"; }; protected: char16_t* fBuffer; @@ -205,6 +207,7 @@ class CString32Converter : public Converter { PyObject* FromMemory(void* address) override; bool ToMemory(PyObject* value, void* address, PyObject* = nullptr) override; bool HasState() override { return true; } + virtual std::string GetFailureMsg() { return "[CString32Converter]"; }; protected: char32_t* fBuffer; @@ -219,11 +222,7 @@ CPPYY_DECLARE_ARRAY_CONVERTER(UChar); CPPYY_DECLARE_ARRAY_CONVERTER(Byte); #endif CPPYY_DECLARE_ARRAY_CONVERTER(Int8); -CPPYY_DECLARE_ARRAY_CONVERTER(Int16); -CPPYY_DECLARE_ARRAY_CONVERTER(Int32); CPPYY_DECLARE_ARRAY_CONVERTER(UInt8); -CPPYY_DECLARE_ARRAY_CONVERTER(UInt16); -CPPYY_DECLARE_ARRAY_CONVERTER(UInt32); CPPYY_DECLARE_ARRAY_CONVERTER(Short); CPPYY_DECLARE_ARRAY_CONVERTER(UShort); CPPYY_DECLARE_ARRAY_CONVERTER(Int); @@ -246,7 +245,7 @@ class CStringArrayConverter : public SCharArrayConverter { using SCharArrayConverter::SCharArrayConverter; bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; PyObject* FromMemory(void* address) override; - bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; + virtual std::string GetFailureMsg() { return "[CStringArrayConverter]"; }; private: std::vector fBuffer; @@ -256,6 +255,7 @@ class NonConstCStringArrayConverter : public CStringArrayConverter { public: using CStringArrayConverter::CStringArrayConverter; PyObject* FromMemory(void* address) override; + virtual std::string GetFailureMsg() { return "[NonConstCStringArrayConverter]"; }; }; // converters for special cases @@ -270,6 +270,7 @@ class InstanceConverter : public StrictInstancePtrConverter { bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; PyObject* FromMemory(void*) override; bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; + virtual std::string GetFailureMsg() { return "[InstanceConverter]"; }; }; class InstanceRefConverter : public Converter { @@ -281,6 +282,7 @@ class InstanceRefConverter : public Converter { bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; PyObject* FromMemory(void* address) override; bool HasState() override { return true; } + virtual std::string GetFailureMsg() { return "[InstanceRefConverter]"; }; protected: Cppyy::TCppType_t fClass; @@ -291,6 +293,7 @@ class InstanceMoveConverter : public InstanceRefConverter { public: InstanceMoveConverter(Cppyy::TCppType_t klass) : InstanceRefConverter(klass, true) {} bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; + virtual std::string GetFailureMsg() { return "[InstanceMoveConverter]"; }; }; template @@ -302,6 +305,7 @@ class InstancePtrPtrConverter : public InstancePtrConverter { bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; PyObject* FromMemory(void* address) override; bool ToMemory(PyObject* value, void* address, PyObject* = nullptr) override; + virtual std::string GetFailureMsg() { return "[InstancePtrPtrConverter]"; }; }; class InstanceArrayConverter : public InstancePtrConverter { @@ -315,6 +319,7 @@ class InstanceArrayConverter : public InstancePtrConverter { bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; PyObject* FromMemory(void* address) override; bool ToMemory(PyObject* value, void* address, PyObject* = nullptr) override; + virtual std::string GetFailureMsg() { return "[InstanceArrayConverter]"; }; protected: dims_t fShape; @@ -330,6 +335,7 @@ class ComplexDConverter: public InstanceConverter { PyObject* FromMemory(void* address) override; bool ToMemory(PyObject* value, void* address, PyObject* = nullptr) override; bool HasState() override { return true; } + virtual std::string GetFailureMsg() { return "[ComplexDConverter]"; }; private: std::complex fBuffer; @@ -341,6 +347,7 @@ class ComplexDConverter: public InstanceConverter { class STLIteratorConverter : public Converter { public: bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; + virtual std::string GetFailureMsg() { return "[STLIteratorConverter]"; }; }; // -- END CLING WORKAROUND @@ -348,20 +355,21 @@ class STLIteratorConverter : public Converter { class VoidPtrRefConverter : public Converter { public: bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; + virtual std::string GetFailureMsg() { return "[VoidPtrRefConverter]"; }; }; class VoidPtrPtrConverter : public Converter { public: - VoidPtrPtrConverter(cdims_t dims); - -public: + VoidPtrPtrConverter(cdims_t dims, const std::string &failureMsg = std::string()); bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; PyObject* FromMemory(void* address) override; bool HasState() override { return true; } + virtual std::string GetFailureMsg() { return "[VoidPtrPtrConverter] " + fFailureMsg; } protected: dims_t fShape; bool fIsFixed; + const std::string fFailureMsg; }; CPPYY_DECLARE_BASIC_CONVERTER(PyObject); @@ -373,11 +381,11 @@ public: \ name##Converter(bool keepControl = true); \ \ public: \ - bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ - PyObject* FromMemory(void* address) override; \ - bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; \ - bool HasState() override { return true; } \ - \ + bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ + PyObject* FromMemory(void* address) override; \ + bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; \ + bool HasState() override { return true; } \ + virtual std::string GetFailureMsg() { return "[" #name "Converter]"; }; \ protected: \ strtype fBuffer; \ } @@ -394,6 +402,7 @@ class STLStringMoveConverter : public STLStringConverter { public: bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; + virtual std::string GetFailureMsg() { return "[STLStringMoveConverter]"; }; }; @@ -408,6 +417,7 @@ class FunctionPointerConverter : public Converter { PyObject* FromMemory(void* address) override; bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; bool HasState() override { return true; } + virtual std::string GetFailureMsg() { return "[FunctionPointerConverter]"; }; protected: std::string fRetType; @@ -427,6 +437,7 @@ class StdFunctionConverter : public FunctionPointerConverter { bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; PyObject* FromMemory(void* address) override; bool ToMemory(PyObject* value, void* address, PyObject* = nullptr) override; + virtual std::string GetFailureMsg() { return "[StdFunctionConverter]"; }; protected: Converter* fConverter; @@ -448,6 +459,7 @@ class SmartPtrConverter : public Converter { PyObject* FromMemory(void* address) override; bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; bool HasState() override { return true; } + virtual std::string GetFailureMsg() { return "[SmartPtrConverter]"; }; protected: virtual bool GetAddressSpecialCase(PyObject*, void*&) { return false; } @@ -470,6 +482,7 @@ class InitializerListConverter : public InstanceConverter { public: bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; bool HasState() override { return true; } + virtual std::string GetFailureMsg() { return "[FunctionPointerConverter]"; }; protected: void Clear(); @@ -486,7 +499,11 @@ class InitializerListConverter : public InstanceConverter { // raising converter to take out overloads class NotImplementedConverter : public Converter { public: + NotImplementedConverter(const std::string &failureMsg = std::string()) : fFailureMsg{failureMsg} {} bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; + virtual std::string GetFailureMsg() { return "[NotImplementedConverter] " + fFailureMsg; } +protected: + const std::string fFailureMsg; }; } // unnamed namespace diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/DeclareExecutors.h b/bindings/pyroot/cppyy/CPyCppyy/src/DeclareExecutors.h index ff6cb18cdc5c9..2b121329af35b 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/DeclareExecutors.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/DeclareExecutors.h @@ -98,24 +98,24 @@ class InstancePtrExecutor : public Executor { bool HasState() override { return true; } protected: - Cppyy::TCppType_t fClass; + Cppyy::TCppScope_t fClass; }; class InstanceExecutor : public Executor { public: - InstanceExecutor(Cppyy::TCppType_t klass); + InstanceExecutor(Cppyy::TCppScope_t klass); PyObject* Execute( Cppyy::TCppMethod_t, Cppyy::TCppObject_t, CallContext*) override; bool HasState() override { return true; } protected: - Cppyy::TCppType_t fClass; - uint32_t fFlags; + Cppyy::TCppScope_t fClass; + uint32_t fFlags; }; class IteratorExecutor : public InstanceExecutor { public: - IteratorExecutor(Cppyy::TCppType_t klass); + IteratorExecutor(Cppyy::TCppScope_t klass); }; CPPYY_DECL_EXEC(Constructor); @@ -150,12 +150,12 @@ CPPYY_DECL_REFEXEC(STLString); // special cases class InstanceRefExecutor : public RefExecutor { public: - InstanceRefExecutor(Cppyy::TCppType_t klass) : fClass(klass) {} + InstanceRefExecutor(Cppyy::TCppScope_t klass) : fClass(klass) {} PyObject* Execute( Cppyy::TCppMethod_t, Cppyy::TCppObject_t, CallContext*) override; protected: - Cppyy::TCppType_t fClass; + Cppyy::TCppScope_t fClass; }; class InstancePtrPtrExecutor : public InstanceRefExecutor { @@ -174,7 +174,7 @@ class InstancePtrRefExecutor : public InstanceRefExecutor { class InstanceArrayExecutor : public InstancePtrExecutor { public: - InstanceArrayExecutor(Cppyy::TCppType_t klass, dim_t array_size) + InstanceArrayExecutor(Cppyy::TCppScope_t klass, dim_t array_size) : InstancePtrExecutor(klass), fSize(array_size) {} PyObject* Execute( Cppyy::TCppMethod_t, Cppyy::TCppObject_t, CallContext*) override; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/DispatchPtr.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/DispatchPtr.cxx index cf766225a08fa..5affdd2120317 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/DispatchPtr.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/DispatchPtr.cxx @@ -10,25 +10,23 @@ //----------------------------------------------------------------------------- PyObject* CPyCppyy::DispatchPtr::Get(bool borrowed) const { - PyGILState_STATE state = PyGILState_Ensure(); - PyObject* result = nullptr; if (fPyHardRef) { if (!borrowed) Py_INCREF(fPyHardRef); - result = fPyHardRef; - } else if (fPyWeakRef) { - result = CPyCppyy_GetWeakRef(fPyWeakRef); - if (result) { // dispatcher object disappeared? - if (borrowed) Py_DECREF(result); + return fPyHardRef; + } + if (fPyWeakRef) { + PyObject* disp = CPyCppyy_GetWeakRef(fPyWeakRef); + if (disp) { // dispatcher object disappeared? + if (borrowed) Py_DECREF(disp); + return disp; } } - PyGILState_Release(state); - return result; + return nullptr; } //----------------------------------------------------------------------------- CPyCppyy::DispatchPtr::DispatchPtr(PyObject* pyobj, bool strong) : fPyHardRef(nullptr) { - PyGILState_STATE state = PyGILState_Ensure(); if (strong) { Py_INCREF(pyobj); fPyHardRef = pyobj; @@ -38,18 +36,15 @@ CPyCppyy::DispatchPtr::DispatchPtr(PyObject* pyobj, bool strong) : fPyHardRef(nu fPyWeakRef = PyWeakref_NewRef(pyobj, nullptr); } ((CPPInstance*)pyobj)->SetDispatchPtr(this); - PyGILState_Release(state); } //----------------------------------------------------------------------------- CPyCppyy::DispatchPtr::DispatchPtr(const DispatchPtr& other, void* cppinst) : fPyWeakRef(nullptr) { - PyGILState_STATE state = PyGILState_Ensure(); PyObject* pyobj = other.Get(false /* not borrowed */); fPyHardRef = pyobj ? (PyObject*)((CPPInstance*)pyobj)->Copy(cppinst) : nullptr; if (fPyHardRef) ((CPPInstance*)fPyHardRef)->SetDispatchPtr(this); Py_XDECREF(pyobj); - PyGILState_Release(state); } //----------------------------------------------------------------------------- @@ -58,7 +53,6 @@ CPyCppyy::DispatchPtr::~DispatchPtr() { // of a dispatcher intermediate, then this delete is from the C++ side, and Python // is "notified" by nulling out the reference and an exception will be raised on // continued access - PyGILState_STATE state = PyGILState_Ensure(); if (fPyWeakRef) { PyObject* pyobj = CPyCppyy_GetWeakRef(fPyWeakRef); if (pyobj && ((CPPScope*)Py_TYPE(pyobj))->fFlags & CPPScope::kIsPython) @@ -69,13 +63,11 @@ CPyCppyy::DispatchPtr::~DispatchPtr() { ((CPPInstance*)fPyHardRef)->GetObjectRaw() = nullptr; Py_DECREF(fPyHardRef); } - PyGILState_Release(state); } //----------------------------------------------------------------------------- CPyCppyy::DispatchPtr& CPyCppyy::DispatchPtr::assign(const DispatchPtr& other, void* cppinst) { - PyGILState_STATE state = PyGILState_Ensure(); if (this != &other) { Py_XDECREF(fPyWeakRef); fPyWeakRef = nullptr; Py_XDECREF(fPyHardRef); @@ -84,7 +76,6 @@ CPyCppyy::DispatchPtr& CPyCppyy::DispatchPtr::assign(const DispatchPtr& other, v if (fPyHardRef) ((CPPInstance*)fPyHardRef)->SetDispatchPtr(this); Py_XDECREF(pyobj); } - PyGILState_Release(state); return *this; } @@ -102,10 +93,8 @@ void CPyCppyy::DispatchPtr::PythonOwns() void CPyCppyy::DispatchPtr::CppOwns() { // C++ maintains the hardref, keeping the PyObject alive w/o outstanding ref - PyGILState_STATE state = PyGILState_Ensure(); if (fPyWeakRef) { fPyHardRef = CPyCppyy_GetWeakRef(fPyWeakRef); Py_DECREF(fPyWeakRef); fPyWeakRef = nullptr; } - PyGILState_Release(state); } diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx index b5f8eb38aed00..228dc03181f8a 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx @@ -19,14 +19,14 @@ static inline void InjectMethod(Cppyy::TCppMethod_t method, const std::string& m using namespace CPyCppyy; // method declaration - std::string retType = Cppyy::GetMethodResultType(method); + std::string retType = Cppyy::GetMethodReturnTypeAsString(method); code << " " << retType << " " << mtCppName << "("; // build out the signature with predictable formal names Cppyy::TCppIndex_t nArgs = Cppyy::GetMethodNumArgs(method); std::vector argtypes; argtypes.reserve(nArgs); for (Cppyy::TCppIndex_t i = 0; i < nArgs; ++i) { - argtypes.push_back(Cppyy::GetMethodArgType(method, i)); + argtypes.push_back(Cppyy::GetMethodArgCanonTypeAsString(method, i)); if (i != 0) code << ", "; code << argtypes.back() << " arg" << i; } @@ -41,24 +41,21 @@ static inline void InjectMethod(Cppyy::TCppMethod_t method, const std::string& m // possible crash code << " PyObject* iself = (PyObject*)_internal_self;\n" " if (!iself || iself == Py_None) {\n" - " PyGILState_STATE state = PyGILState_Ensure();\n" " PyErr_Warn(PyExc_RuntimeWarning, (char*)\"Call attempted on deleted python-side proxy\");\n" - " PyGILState_Release(state);\n" " return"; if (retType != "void") { if (retType.back() != '*') - code << " (" << CPyCppyy::TypeManip::remove_const(retType) << "){}"; + code << " " << CPyCppyy::TypeManip::remove_const(retType) << "{}"; else code << " nullptr"; } code << ";\n" - " }\n"; + " }\n" + " Py_INCREF(iself);\n"; // start actual function body Utility::ConstructCallbackPreamble(retType, argtypes, code); - code << " Py_INCREF(iself);\n"; - // perform actual method call #if PY_VERSION_HEX < 0x03000000 code << " PyObject* mtPyName = PyString_FromString(\"" << mtCppName << "\");\n" // TODO: intern? @@ -120,7 +117,7 @@ static void build_constructors( size_t offset = (i != 0 ? arg_tots[i-1] : 0); for (size_t j = 0; j < nArgs; ++j) { if (i != 0 || j != 0) code << ", "; - code << Cppyy::GetMethodArgType(cinfo.first, j) << " a" << (j+offset); + code << Cppyy::GetMethodArgCanonTypeAsString(cinfo.first, j) << " a" << (j+offset); } } code << ") : "; @@ -133,7 +130,7 @@ static void build_constructors( for (size_t j = first; j < arg_tots[i]; ++j) { if (j != first) code << ", "; bool isRValue = CPyCppyy::TypeManip::compound(\ - Cppyy::GetMethodArgType(methods[i].first, j-first)) == "&&"; + Cppyy::GetMethodArgCanonTypeAsString(methods[i].first, j-first)) == "&&"; if (isRValue) code << "std::move("; code << "a" << j; if (isRValue) code << ")"; @@ -149,14 +146,14 @@ namespace { using namespace Cppyy; static inline -std::vector FindBaseMethod(TCppScope_t tbase, const std::string mtCppName) +std::vector FindBaseMethod(TCppScope_t tbase, const std::string mtCppName) { // Recursively walk the inheritance tree to find the overloads of the named method - std::vector result; - result = GetMethodIndicesFromName(tbase, mtCppName); + std::vector result; + result = GetMethodsFromName(tbase, mtCppName); if (result.empty()) { for (TCppIndex_t ibase = 0; ibase < GetNumBases(tbase); ++ibase) { - TCppScope_t b = GetScope(GetBaseName(tbase, ibase)); + TCppScope_t b = Cppyy::GetBaseScope(tbase, ibase); result = FindBaseMethod(b, mtCppName); if (!result.empty()) break; @@ -241,7 +238,6 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, // object goes before the C++ one, only __del__ is called) if (PyMapping_HasKeyString(dct, (char*)"__destruct__")) { code << " virtual ~" << derivedName << "() {\n" - "PyGILState_STATE state = PyGILState_Ensure();\n" " PyObject* iself = (PyObject*)_internal_self;\n" " if (!iself || iself == Py_None)\n" " return;\n" // safe, as destructor always returns void @@ -254,7 +250,6 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, // magic C++ exception ... code << " if (!pyresult) PyErr_Print();\n" " else { Py_DECREF(pyresult); }\n" - " PyGILState_Release(state);\n" " }\n"; } else code << " virtual ~" << derivedName << "() {}\n"; @@ -282,18 +277,18 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, for (BaseInfos_t::size_type ibase = 0; ibase < base_infos.size(); ++ibase) { const auto& binfo = base_infos[ibase]; - const Cppyy::TCppIndex_t nMethods = Cppyy::GetNumMethods(binfo.btype); + std::vector methods; + Cppyy::GetClassMethods(binfo.btype, methods); bool cctor_found = false, default_found = false, any_ctor_found = false; - for (Cppyy::TCppIndex_t imeth = 0; imeth < nMethods; ++imeth) { - Cppyy::TCppMethod_t method = Cppyy::GetMethod(binfo.btype, imeth); - + for (auto &method : methods) { if (Cppyy::IsConstructor(method)) { any_ctor_found = true; - if (Cppyy::IsPublicMethod(method) || Cppyy::IsProtectedMethod(method)) { + if ((Cppyy::IsPublicMethod(method) || Cppyy::IsProtectedMethod(method)) && + !Cppyy::IsDeletedMethod(method)) { Cppyy::TCppIndex_t nreq = Cppyy::GetMethodReqArgs(method); if (nreq == 0) default_found = true; else if (!cctor_found && nreq == 1) { - const std::string& argtype = Cppyy::GetMethodArgType(method, 0); + const std::string& argtype = Cppyy::GetMethodArgCanonTypeAsString(method, 0); if (TypeManip::compound(argtype) == "&" && TypeManip::clean_type(argtype, false) == binfo.bname_scoped) cctor_found = true; } @@ -314,11 +309,11 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, if (Cppyy::IsProtectedMethod(method)) { protected_names.insert(mtCppName); - code << " " << Cppyy::GetMethodResultType(method) << " " << mtCppName << "("; + code << " " << Cppyy::GetMethodReturnTypeAsString(method) << " " << mtCppName << "("; Cppyy::TCppIndex_t nArgs = Cppyy::GetMethodNumArgs(method); for (Cppyy::TCppIndex_t i = 0; i < nArgs; ++i) { if (i != 0) code << ", "; - code << Cppyy::GetMethodArgType(method, i) << " arg" << i; + code << Cppyy::GetMethodArgCanonTypeAsString(method, i) << " arg" << i; } code << ") "; if (Cppyy::IsConstMethod(method)) code << "const "; @@ -342,9 +337,10 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, // support for templated ctors in single inheritance (TODO: also multi possible?) if (base_infos.size() == 1) { - const Cppyy::TCppIndex_t nTemplMethods = Cppyy::GetNumTemplatedMethods(binfo.btype); - for (Cppyy::TCppIndex_t imeth = 0; imeth < nTemplMethods; ++imeth) { - if (Cppyy::IsTemplatedConstructor(binfo.btype, imeth)) { + std::vector templ_methods; + Cppyy::GetTemplatedMethods(binfo.btype, templ_methods); + for (auto &method : templ_methods) { + if (Cppyy::IsConstructor(method)) { any_ctor_found = true; has_tmpl_ctors += 1; break; // one suffices to map as argument packs are used @@ -363,18 +359,18 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, if (PyDict_Size(clbs)) { size_t nbases = Cppyy::GetNumBases(binfo.btype); for (size_t ibase = 0; ibase < nbases; ++ibase) { - Cppyy::TCppScope_t tbase = (Cppyy::TCppScope_t)Cppyy::GetScope( \ - Cppyy::GetBaseName(binfo.btype, ibase)); + Cppyy::TCppScope_t tbase = + (Cppyy::TCppScope_t) Cppyy::GetBaseScope(binfo.btype, ibase); PyObject* keys = PyDict_Keys(clbs); for (Py_ssize_t i = 0; i < PyList_GET_SIZE(keys); ++i) { // TODO: should probably invert this looping; but that makes handling overloads clunky PyObject* key = PyList_GET_ITEM(keys, i); std::string mtCppName = CPyCppyy_PyText_AsString(key); - const auto& v = FindBaseMethod(tbase, mtCppName); - for (auto idx : v) - InjectMethod(Cppyy::GetMethod(tbase, idx), mtCppName, code); - if (!v.empty()) { + const auto& methods = FindBaseMethod(tbase, mtCppName); + for (auto method : methods) + InjectMethod(method, mtCppName, code); + if (!methods.empty()) { if (PyDict_DelItem(clbs, key) != 0) PyErr_Clear(); } } @@ -415,12 +411,13 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, // pull in data members that are protected bool setPublic = false; for (const auto& binfo : base_infos) { - Cppyy::TCppIndex_t nData = Cppyy::GetNumDatamembers(binfo.btype); - for (Cppyy::TCppIndex_t idata = 0; idata < nData; ++idata) { - if (Cppyy::IsProtectedData(binfo.btype, idata)) { - const std::string dm_name = Cppyy::GetDatamemberName(binfo.btype, idata); + std::vector datamems; + Cppyy::GetDatamembers(binfo.btype, datamems); + for (auto data : datamems) { + if (Cppyy::IsProtectedData(data)) { + const std::string dm_name = Cppyy::GetFinalName(data); if (dm_name != "_internal_self") { - const std::string& dname = Cppyy::GetDatamemberName(binfo.btype, idata); + const std::string& dname = Cppyy::GetFinalName(data); protected_names.insert(dname); if (!setPublic) { code << "public:\n"; @@ -437,7 +434,7 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, code << "public:\n static void _init_dispatchptr(" << derivedName << "* inst, PyObject* self) {\n"; if (1 < base_infos.size()) { for (const auto& binfo : base_infos) { - if (Cppyy::GetDatamemberIndex(binfo.btype, "_internal_self") != (Cppyy::TCppIndex_t)-1) { + if (Cppyy::CheckDatamember(binfo.btype, "_internal_self")) { code << " " << binfo.bname << "::_init_dispatchptr(inst, self);\n"; disp_inited += 1; } @@ -455,11 +452,8 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, // provide an accessor to re-initialize after round-tripping from C++ (internal) code << "\n static PyObject* _get_dispatch(" << derivedName << "* inst) {\n" - " PyGILState_STATE state = PyGILState_Ensure();\n" " PyObject* res = (PyObject*)inst->_internal_self;\n" - " Py_XINCREF(res);\n" - " PyGILState_Release(state);\n" - " return res;\n }"; + " Py_XINCREF(res); return res;\n }"; // finish class declaration code << "};\n}"; @@ -472,7 +466,7 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, // keep track internally of the actual C++ type (this is used in // CPPConstructor to call the dispatcher's one instead of the base) - Cppyy::TCppScope_t disp = Cppyy::GetScope("__cppyy_internal::"+derivedName); + Cppyy::TCppScope_t disp = Cppyy::GetFullScope("__cppyy_internal::"+derivedName); if (!disp) { err << "failed to retrieve the internal dispatcher"; return false; @@ -483,7 +477,7 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, // that are part of the hierarchy in Python, so create it, which will cache it for // later use by e.g. the MemoryRegulator unsigned int flags = (unsigned int)(klass->fFlags & CPPScope::kIsMultiCross); - PyObject* disp_proxy = CPyCppyy::CreateScopeProxy(disp, flags); + PyObject* disp_proxy = CPyCppyy::CreateScopeProxy(disp, 0, flags); if (flags) ((CPPScope*)disp_proxy)->fFlags |= CPPScope::kIsMultiCross; ((CPPScope*)disp_proxy)->fFlags |= CPPScope::kIsPython; @@ -492,12 +486,7 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, // Python class to keep the inheritance tree intact) for (const auto& name : protected_names) { PyObject* disp_dct = PyObject_GetAttr(disp_proxy, PyStrings::gDict); -#if PY_VERSION_HEX < 0x30d00f0 PyObject* pyf = PyMapping_GetItemString(disp_dct, (char*)name.c_str()); -#else - PyObject* pyf = nullptr; - PyMapping_GetOptionalItemString(disp_dct, (char*)name.c_str(), &pyf); -#endif if (pyf) { PyObject_SetAttrString((PyObject*)klass, (char*)name.c_str(), pyf); Py_DECREF(pyf); diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Executors.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/Executors.cxx index 954be578485b0..b52c96f68d2d2 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Executors.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Executors.cxx @@ -1,5 +1,6 @@ // Bindings #include "CPyCppyy.h" +#include "Cppyy.h" #include "DeclareExecutors.h" #include "CPPInstance.h" #include "LowLevelViews.h" @@ -78,20 +79,21 @@ CPPYY_IMPL_GILCALL(PY_LONG_DOUBLE, LD) CPPYY_IMPL_GILCALL(void*, R) static inline Cppyy::TCppObject_t GILCallO(Cppyy::TCppMethod_t method, - Cppyy::TCppObject_t self, CPyCppyy::CallContext* ctxt, Cppyy::TCppType_t klass) + Cppyy::TCppObject_t self, CPyCppyy::CallContext* ctxt, Cppyy::TCppScope_t klass) { + Cppyy::TCppType_t klass_ty = Cppyy::GetTypeFromScope(klass); #ifdef WITH_THREAD if (!ReleasesGIL(ctxt)) #endif - return Cppyy::CallO(method, self, ctxt->GetEncodedSize(), ctxt->GetArgs(), klass); + return Cppyy::CallO(method, self, ctxt->GetEncodedSize(), ctxt->GetArgs(), klass_ty); #ifdef WITH_THREAD GILControl gc{}; - return Cppyy::CallO(method, self, ctxt->GetEncodedSize(), ctxt->GetArgs(), klass); + return Cppyy::CallO(method, self, ctxt->GetEncodedSize(), ctxt->GetArgs(), klass_ty); #endif } static inline Cppyy::TCppObject_t GILCallConstructor( - Cppyy::TCppMethod_t method, Cppyy::TCppType_t klass, CPyCppyy::CallContext* ctxt) + Cppyy::TCppMethod_t method, Cppyy::TCppScope_t klass, CPyCppyy::CallContext* ctxt) { #ifdef WITH_THREAD if (!ReleasesGIL(ctxt)) @@ -389,9 +391,17 @@ PyObject* CPyCppyy::STLStringRefExecutor::Execute( Cppyy::TCppMethod_t method, Cppyy::TCppObject_t self, CallContext* ctxt) { // execute with argument , return python string return value + static Cppyy::TCppScope_t sSTLStringScope = Cppyy::GetFullScope("std::string"); + std::string* result = (std::string*)GILCallR(method, self, ctxt); if (!fAssignable) { - return CPyCppyy_PyText_FromStringAndSize(result->c_str(), result->size()); + std::string* rescp = new std::string{*result}; + return BindCppObjectNoCast((void*)rescp, sSTLStringScope, CPPInstance::kIsOwner); + } + + if (!CPyCppyy_PyText_Check(fAssignable)) { + PyErr_Format(PyExc_TypeError, "wrong type in assignment (string expected)"); + return nullptr; } *result = std::string( @@ -558,18 +568,16 @@ PyObject* CPyCppyy::STLStringExecutor::Execute( // execute with argument , construct python string return value // TODO: make use of GILLCallS (?!) - static Cppyy::TCppScope_t sSTLStringScope = Cppyy::GetScope("std::string"); + static Cppyy::TCppScope_t sSTLStringScope = Cppyy::GetFullScope("std::string"); std::string* result = (std::string*)GILCallO(method, self, ctxt, sSTLStringScope); - if (!result) { - Py_INCREF(PyStrings::gEmptyString); - return PyStrings::gEmptyString; + if (!result) + result = new std::string{}; + else if (PyErr_Occurred()) { + delete result; + return nullptr; } - PyObject* pyresult = - CPyCppyy_PyText_FromStringAndSize(result->c_str(), result->size()); - delete result; // Cppyy::CallO allocates and constructs a string, so it must be properly destroyed - - return pyresult; + return BindCppObjectNoCast((void*)result, sSTLStringScope, CPPInstance::kIsOwner); } //---------------------------------------------------------------------------- @@ -577,7 +585,7 @@ PyObject* CPyCppyy::STLWStringExecutor::Execute( Cppyy::TCppMethod_t method, Cppyy::TCppObject_t self, CallContext* ctxt) { // execute with argument , construct python string return value - static Cppyy::TCppScope_t sSTLWStringScope = Cppyy::GetScope("std::wstring"); + static Cppyy::TCppScope_t sSTLWStringScope = Cppyy::GetFullScope("std::wstring"); std::wstring* result = (std::wstring*)GILCallO(method, self, ctxt, sSTLWStringScope); if (!result) { wchar_t w = L'\0'; @@ -599,7 +607,7 @@ PyObject* CPyCppyy::InstancePtrExecutor::Execute( } //---------------------------------------------------------------------------- -CPyCppyy::InstanceExecutor::InstanceExecutor(Cppyy::TCppType_t klass) : +CPyCppyy::InstanceExecutor::InstanceExecutor(Cppyy::TCppScope_t klass) : fClass(klass), fFlags(CPPInstance::kIsValue | CPPInstance::kIsOwner) { /* empty */ @@ -630,7 +638,7 @@ PyObject* CPyCppyy::InstanceExecutor::Execute( //---------------------------------------------------------------------------- -CPyCppyy::IteratorExecutor::IteratorExecutor(Cppyy::TCppType_t klass) : +CPyCppyy::IteratorExecutor::IteratorExecutor(Cppyy::TCppScope_t klass) : InstanceExecutor(klass) { fFlags |= CPPInstance::kNoMemReg | CPPInstance::kNoWrapConv; // adds to flags from base class @@ -750,7 +758,7 @@ PyObject* CPyCppyy::ConstructorExecutor::Execute( { // package return address in PyObject* for caller to handle appropriately (see // CPPConstructor for the actual build of the PyObject) - return (PyObject*)GILCallConstructor(method, (Cppyy::TCppType_t)klass, ctxt); + return (PyObject*)GILCallConstructor(method, (Cppyy::TCppScope_t)klass, ctxt); } //---------------------------------------------------------------------------- @@ -788,13 +796,16 @@ CPyCppyy::Executor* CPyCppyy::CreateExecutor(const std::string& fullType, cdims_ // // If all fails, void is used, which will cause the return type to be ignored on use + if (fullType.empty()) + return nullptr; + // an exactly matching executor is best ExecFactories_t::iterator h = gExecFactories.find(fullType); if (h != gExecFactories.end()) return (h->second)(dims); // resolve typedefs etc. - const std::string& resolvedType = Cppyy::ResolveName(fullType); + const std::string resolvedType = Cppyy::ResolveName(fullType); // a full, qualified matching executor is preferred if (resolvedType != fullType) { @@ -838,7 +849,7 @@ CPyCppyy::Executor* CPyCppyy::CreateExecutor(const std::string& fullType, cdims_ // C++ classes and special cases Executor* result = 0; - if (Cppyy::TCppType_t klass = Cppyy::GetScope(realType)) { + if (Cppyy::TCppType_t klass = Cppyy::GetFullScope(realType)) { if (Utility::IsSTLIterator(realType) || gIteratorTypes.find(fullType) != gIteratorTypes.end()) { if (cpd == "") return new IteratorExecutor(klass); @@ -881,6 +892,131 @@ CPyCppyy::Executor* CPyCppyy::CreateExecutor(const std::string& fullType, cdims_ return result; // may still be null } +CPyCppyy::Executor* CPyCppyy::CreateExecutor(Cppyy::TCppType_t type, cdims_t dims) +{ +// The matching of the fulltype to an executor factory goes through up to 4 levels: +// 1) full, qualified match +// 2) drop '&' as by ref/full type is often pretty much the same python-wise +// 3) C++ classes, either by ref/ptr or by value +// 4) additional special case for enums +// +// If all fails, void is used, which will cause the return type to be ignored on use + +// an exactly matching executor is best + std::string fullType = Cppyy::GetTypeAsString(type); + if (fullType.ends_with(" &")) + fullType = fullType.substr(0, fullType.size() - 2) + "&"; + + ExecFactories_t::iterator h = gExecFactories.find(fullType); + if (h != gExecFactories.end()) + return (h->second)(dims); + +// resolve typedefs etc. + Cppyy::TCppType_t resolvedType = Cppyy::ResolveType(type); + { + // if resolvedType is a reference to enum + // then it should be reduced to reference + // to the underlying interger + resolvedType = Cppyy::ResolveEnumReferenceType(resolvedType); + // similarly for pointers + resolvedType = Cppyy::ResolveEnumPointerType(resolvedType); + } + // FIXME: avoid string comparisons and parsing + std::string resolvedTypeStr = Cppyy::GetTypeAsString(resolvedType); + if (Cppyy::IsFunctionPointerType(resolvedType)) { + resolvedTypeStr.erase(std::remove(resolvedTypeStr.begin(), resolvedTypeStr.end(), ' '), resolvedTypeStr.end()); + if (resolvedTypeStr.rfind("(void)") != std::string::npos) + resolvedTypeStr = resolvedTypeStr.substr(0, resolvedTypeStr.size() - 6) + "()"; + } + +// a full, qualified matching executor is preferred + if (resolvedTypeStr != fullType) { + h = gExecFactories.find(resolvedTypeStr); + if (h != gExecFactories.end()) + return (h->second)(dims); + } + +//-- nothing? ok, collect information about the type and possible qualifiers/decorators + bool isConst = strncmp(resolvedTypeStr.c_str(), "const", 5) == 0; + const std::string& cpd = TypeManip::compound(resolvedTypeStr); + Cppyy::TCppType_t realType = Cppyy::IsFunctionPointerType(resolvedType) ? resolvedType : Cppyy::GetRealType(resolvedType); + std::string realTypeStr = Cppyy::IsFunctionPointerType(resolvedType) ? resolvedTypeStr : Cppyy::GetTypeAsString(realType); + const std::string compounded = cpd.empty() ? realTypeStr : realTypeStr + cpd; + +// accept unqualified type (as python does not know about qualifiers) + h = gExecFactories.find(compounded); + if (h != gExecFactories.end()) + return (h->second)(dims); + +// drop const, as that is mostly meaningless to python (with the exception +// of c-strings, but those are specialized in the converter map) + if (isConst) { + realTypeStr = TypeManip::remove_const(realTypeStr); + h = gExecFactories.find(compounded); + if (h != gExecFactories.end()) + return (h->second)(dims); + } + +// simple array types + if (!cpd.empty() && (std::string::size_type)std::count(cpd.begin(), cpd.end(), '*') == cpd.size()) { + h = gExecFactories.find(realTypeStr + " ptr"); + if (h != gExecFactories.end()) + return (h->second)((!dims || dims.ndim() < (dim_t)cpd.size()) ? dims_t(cpd.size()) : dims); + } + +//-- still nothing? try pointer instead of array (for builtins) + if (cpd == "[]") { + h = gExecFactories.find(realTypeStr + "*"); + if (h != gExecFactories.end()) + return (h->second)(dims); + } + +// C++ classes and special cases + Executor* result = 0; + if (Cppyy::IsClassType(realType)) { + Cppyy::TCppScope_t klass = Cppyy::GetScopeFromType(realType); + if (resolvedTypeStr.find("iterator") != std::string::npos || gIteratorTypes.find(fullType) != gIteratorTypes.end()) { + if (cpd == "") + return new IteratorExecutor(klass); + } + + if (cpd == "") + result = new InstanceExecutor(klass); + else if (cpd == "&") + result = new InstanceRefExecutor(klass); + else if (cpd == "**" || cpd == "*[]" || cpd == "&*") + result = new InstancePtrPtrExecutor(klass); + else if (cpd == "*&") + result = new InstancePtrRefExecutor(klass); + else if (cpd == "[]") { + Py_ssize_t asize = TypeManip::array_size(resolvedTypeStr); + if (0 < asize) + result = new InstanceArrayExecutor(klass, asize); + else + result = new InstancePtrRefExecutor(klass); + } else + result = new InstancePtrExecutor(klass); + } else if (realTypeStr.find("(*)") != std::string::npos || + (realTypeStr.find("::*)") != std::string::npos)) { + // this is a function pointer + // TODO: find better way of finding the type + auto pos1 = realTypeStr.find('('); + auto pos2 = realTypeStr.find("*)"); + auto pos3 = realTypeStr.rfind(')'); + result = new FunctionPointerExecutor( + realTypeStr.substr(0, pos1), realTypeStr.substr(pos2+2, pos3-pos2-1)); + } else { + // unknown: void* may work ("user knows best"), void will fail on use of return value + h = (cpd == "") ? gExecFactories.find("void") : gExecFactories.find("void ptr"); + } + + if (!result && h != gExecFactories.end()) + // executor factory available, use it to create executor + result = (h->second)(dims); + + return result; // may still be null +} + //---------------------------------------------------------------------------- CPYCPPYY_EXPORT void CPyCppyy::DestroyExecutor(Executor* p) @@ -1022,8 +1158,6 @@ struct InitExecFactories_t { #if (__cplusplus > 201402L) || (defined(_MSC_VER) && _MSVC_LANG > 201402L) gf["std::byte ptr"] = (ef_t)+[](cdims_t d) { return new ByteArrayExecutor{d}; }; gf["const std::byte ptr"] = gf["std::byte ptr"]; - gf["byte ptr"] = gf["std::byte ptr"]; - gf["const byte ptr"] = gf["std::byte ptr"]; #endif gf["int8_t ptr"] = (ef_t)+[](cdims_t d) { return new Int8ArrayExecutor{d}; }; gf["uint8_t ptr"] = (ef_t)+[](cdims_t d) { return new UInt8ArrayExecutor{d}; }; @@ -1048,11 +1182,8 @@ struct InitExecFactories_t { gf["internal_enum_type_t ptr"] = gf["int ptr"]; #if (__cplusplus > 201402L) || (defined(_MSC_VER) && _MSVC_LANG > 201402L) gf["std::byte"] = gf["uint8_t"]; - gf["byte"] = gf["uint8_t"]; gf["std::byte&"] = gf["uint8_t&"]; - gf["byte&"] = gf["uint8_t&"]; gf["const std::byte&"] = gf["const uint8_t&"]; - gf["const byte&"] = gf["const uint8_t&"]; #endif gf["std::int8_t"] = gf["int8_t"]; gf["std::int8_t&"] = gf["int8_t&"]; @@ -1086,13 +1217,11 @@ struct InitExecFactories_t { gf["wchar_t*"] = (ef_t)+[](cdims_t) { static WCStringExecutor e{}; return &e;}; gf["char16_t*"] = (ef_t)+[](cdims_t) { static CString16Executor e{}; return &e;}; gf["char32_t*"] = (ef_t)+[](cdims_t) { static CString32Executor e{}; return &e;}; - gf["std::string"] = (ef_t)+[](cdims_t) { static STLStringExecutor e{}; return &e; }; - gf["string"] = gf["std::string"]; - gf["std::string&"] = (ef_t)+[](cdims_t) { return new STLStringRefExecutor{}; }; - gf["string&"] = gf["std::string&"]; - gf["std::wstring"] = (ef_t)+[](cdims_t) { static STLWStringExecutor e{}; return &e; }; - gf[WSTRING1] = gf["std::wstring"]; - gf[WSTRING2] = gf["std::wstring"]; + gf["std::basic_string"] = (ef_t)+[](cdims_t) { static STLStringExecutor e{}; return &e; }; + gf["std::basic_string&"] = (ef_t)+[](cdims_t) { return new STLStringRefExecutor{}; }; + gf["std::basic_string"] = (ef_t)+[](cdims_t) { static STLWStringExecutor e{}; return &e; }; + gf[WSTRING1] = gf["std::basic_string"]; + gf[WSTRING2] = gf["std::basic_string"]; gf["__init__"] = (ef_t)+[](cdims_t) { static ConstructorExecutor e{}; return &e; }; gf["PyObject*"] = (ef_t)+[](cdims_t) { static PyObjectExecutor e{}; return &e; }; gf["_object*"] = gf["PyObject*"]; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Executors.h b/bindings/pyroot/cppyy/CPyCppyy/src/Executors.h index e043bf164fa55..50eab0a336c04 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Executors.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Executors.h @@ -33,6 +33,7 @@ class RefExecutor : public Executor { // create/destroy executor from fully qualified type (public API) CPYCPPYY_EXPORT Executor* CreateExecutor(const std::string& fullType, cdims_t = 0); +CPYCPPYY_EXPORT Executor* CreateExecutor(Cppyy::TCppType_t type, cdims_t = 0); CPYCPPYY_EXPORT void DestroyExecutor(Executor* p); typedef Executor* (*ef_t) (cdims_t); CPYCPPYY_EXPORT bool RegisterExecutor(const std::string& name, ef_t fac); diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/LowLevelViews.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/LowLevelViews.cxx index 3b0d48e9f1c7c..c3f8aaf51f81f 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/LowLevelViews.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/LowLevelViews.cxx @@ -1200,43 +1200,3 @@ PyObject* CPyCppyy::CreateLowLevelView_i8(uint8_t** address, cdims_t shape) { LowLevelView* ll = CreateLowLevelViewT(address, shape, "B", "uint8_t"); CPPYY_RET_W_CREATOR(uint8_t**, CreateLowLevelView_i8); } - -PyObject* CPyCppyy::CreateLowLevelView_i16(int16_t* address, cdims_t shape) { - LowLevelView* ll = CreateLowLevelViewT(address, shape, "h", "int16_t"); - CPPYY_RET_W_CREATOR(int16_t*, CreateLowLevelView_i16); -} - -PyObject* CPyCppyy::CreateLowLevelView_i16(int16_t** address, cdims_t shape) { - LowLevelView* ll = CreateLowLevelViewT(address, shape, "h", "int16_t"); - CPPYY_RET_W_CREATOR(int16_t**, CreateLowLevelView_i16); -} - -PyObject* CPyCppyy::CreateLowLevelView_i16(uint16_t* address, cdims_t shape) { - LowLevelView* ll = CreateLowLevelViewT(address, shape, "H", "uint16_t"); - CPPYY_RET_W_CREATOR(uint16_t*, CreateLowLevelView_i16); -} - -PyObject* CPyCppyy::CreateLowLevelView_i16(uint16_t** address, cdims_t shape) { - LowLevelView* ll = CreateLowLevelViewT(address, shape, "H", "uint16_t"); - CPPYY_RET_W_CREATOR(uint16_t**, CreateLowLevelView_i16); -} - -PyObject* CPyCppyy::CreateLowLevelView_i32(int32_t* address, cdims_t shape) { - LowLevelView* ll = CreateLowLevelViewT(address, shape, "i", "int32_t"); - CPPYY_RET_W_CREATOR(int32_t*, CreateLowLevelView_i32); -} - -PyObject* CPyCppyy::CreateLowLevelView_i32(int32_t** address, cdims_t shape) { - LowLevelView* ll = CreateLowLevelViewT(address, shape, "i", "int32_t"); - CPPYY_RET_W_CREATOR(int32_t**, CreateLowLevelView_i32); -} - -PyObject* CPyCppyy::CreateLowLevelView_i32(uint32_t* address, cdims_t shape) { - LowLevelView* ll = CreateLowLevelViewT(address, shape, "I", "uint32_t"); - CPPYY_RET_W_CREATOR(uint32_t*, CreateLowLevelView_i32); -} - -PyObject* CPyCppyy::CreateLowLevelView_i32(uint32_t** address, cdims_t shape) { - LowLevelView* ll = CreateLowLevelViewT(address, shape, "I", "uint32_t"); - CPPYY_RET_W_CREATOR(uint32_t**, CreateLowLevelView_i32); -} diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/LowLevelViews.h b/bindings/pyroot/cppyy/CPyCppyy/src/LowLevelViews.h index 811af69e2787f..4186ea09317fc 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/LowLevelViews.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/LowLevelViews.h @@ -56,15 +56,6 @@ PyObject* CreateLowLevelView_i8(int8_t*, cdims_t shape); PyObject* CreateLowLevelView_i8(int8_t**, cdims_t shape); PyObject* CreateLowLevelView_i8(uint8_t*, cdims_t shape); PyObject* CreateLowLevelView_i8(uint8_t**, cdims_t shape); -PyObject* CreateLowLevelView_i16(int16_t*, cdims_t shape); -PyObject* CreateLowLevelView_i16(int16_t**, cdims_t shape); -PyObject* CreateLowLevelView_i16(uint16_t*, cdims_t shape); -PyObject* CreateLowLevelView_i16(uint16_t**, cdims_t shape); -PyObject* CreateLowLevelView_i32(int32_t*, cdims_t shape); -PyObject* CreateLowLevelView_i32(int32_t**, cdims_t shape); -PyObject* CreateLowLevelView_i32(uint32_t*, cdims_t shape); -PyObject* CreateLowLevelView_i32(uint32_t**, cdims_t shape); - CPPYY_DECL_VIEW_CREATOR(short); CPPYY_DECL_VIEW_CREATOR(unsigned short); CPPYY_DECL_VIEW_CREATOR(int); diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/MemoryRegulator.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/MemoryRegulator.cxx index 52d8261bc1fda..8103c1cb1357a 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/MemoryRegulator.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/MemoryRegulator.cxx @@ -35,25 +35,13 @@ static PyMappingMethods CPyCppyy_NoneType_mapping = { //----------------------------------------------------------------------------- namespace { -// Py_SET_REFCNT was only introduced in Python 3.9 -#if PY_VERSION_HEX < 0x03090000 -inline void Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { - assert(refcnt >= 0); -#if SIZEOF_VOID_P > 4 - ob->ob_refcnt = (PY_UINT32_T)refcnt; -#else - ob->ob_refcnt = refcnt; -#endif -} -#endif - struct InitCPyCppyy_NoneType_t { InitCPyCppyy_NoneType_t() { // create a CPyCppyy NoneType (for references that went dodo) from NoneType memset(&CPyCppyy_NoneType, 0, sizeof(CPyCppyy_NoneType)); ((PyObject&)CPyCppyy_NoneType).ob_type = &PyType_Type; - Py_SET_REFCNT((PyObject*)&CPyCppyy_NoneType, 1); + ((PyObject&)CPyCppyy_NoneType).ob_refcnt = 1; ((PyVarObject&)CPyCppyy_NoneType).ob_size = 0; CPyCppyy_NoneType.tp_name = const_cast("CPyCppyy_NoneType"); @@ -159,10 +147,10 @@ bool CPyCppyy::MemoryRegulator::RecursiveRemove( } // notify any other weak referents by playing dead - Py_ssize_t refcnt = Py_REFCNT((PyObject*)pyobj); - Py_SET_REFCNT((PyObject*)pyobj, 0); + Py_ssize_t refcnt = ((PyObject*)pyobj)->ob_refcnt; + ((PyObject*)pyobj)->ob_refcnt = 0; PyObject_ClearWeakRefs((PyObject*)pyobj); - Py_SET_REFCNT((PyObject*)pyobj, refcnt); + ((PyObject*)pyobj)->ob_refcnt = refcnt; // cleanup object internals pyobj->CppOwns(); // held object is out of scope now anyway diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.cxx index 35a76b0552763..4e809d94e3769 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.cxx @@ -90,16 +90,16 @@ static PyObject* CreateNewCppProxyClass(Cppyy::TCppScope_t klass, PyObject* pyba static inline void AddPropertyToClass(PyObject* pyclass, - Cppyy::TCppScope_t scope, Cppyy::TCppIndex_t idata) + Cppyy::TCppScope_t scope, Cppyy::TCppScope_t data) { - CPyCppyy::CPPDataMember* property = CPyCppyy::CPPDataMember_New(scope, idata); + CPyCppyy::CPPDataMember* property = CPyCppyy::CPPDataMember_New(scope, data); PyObject* pname = CPyCppyy_PyText_InternFromString(const_cast(property->GetName().c_str())); // allow access at the instance level PyType_Type.tp_setattro(pyclass, pname, (PyObject*)property); // allow access at the class level (always add after setting instance level) - if (Cppyy::IsStaticData(scope, idata)) + if (Cppyy::IsStaticDatamember(data) || Cppyy::IsEnumConstant(data)) PyType_Type.tp_setattro((PyObject*)Py_TYPE(pyclass), pname, (PyObject*)property); // cleanup @@ -159,7 +159,7 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons // some properties that'll affect building the dictionary bool isNamespace = Cppyy::IsNamespace(scope); - bool isAbstract = Cppyy::IsAbstract(scope); + bool isComplete = Cppyy::IsComplete(scope); bool hasConstructor = false; Cppyy::TCppMethod_t potGetItem = (Cppyy::TCppMethod_t)0; @@ -174,9 +174,10 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons // functions in namespaces are properly found through lazy lookup, so do not // create them until needed (the same is not true for data members) - const Cppyy::TCppIndex_t nMethods = isNamespace ? 0 : Cppyy::GetNumMethods(scope); - for (Cppyy::TCppIndex_t imeth = 0; imeth < nMethods; ++imeth) { - Cppyy::TCppMethod_t method = Cppyy::GetMethod(scope, imeth); + std::vector methods; + if (isComplete) Cppyy::GetClassMethods(scope, methods); + + for (auto &method : methods) { // do not expose non-public methods as the Cling wrappers as those won't compile if (!Cppyy::IsPublicMethod(method)) @@ -188,7 +189,9 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons // special case trackers bool setupSetItem = false; bool isConstructor = Cppyy::IsConstructor(method); - bool isTemplate = isConstructor ? false : Cppyy::IsMethodTemplate(scope, imeth); + + // Here isTemplate means to ignore templated constructors, that is handled in the next loop. + bool isTemplate = Cppyy::IsTemplatedMethod(method) && !isConstructor; bool isStubbedOperator = false; // filter empty names (happens for namespaces, is bug?) @@ -208,7 +211,7 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons // operator[]/() returning a reference type will be used for __setitem__ bool isCall = mtName == "__call__"; if (isCall || mtName == "__getitem__") { - const std::string& qual_return = Cppyy::ResolveName(Cppyy::GetMethodResultType(method)); + const std::string& qual_return = Cppyy::GetMethodReturnTypeAsString(method); const std::string& cpd = TypeManip::compound(qual_return); if (!cpd.empty() && cpd[cpd.size()-1] == '&' && \ qual_return.find("const", 0, 5) == std::string::npos) { @@ -224,8 +227,7 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons } // template members; handled by adding a dispatcher to the class - bool storeOnTemplate = - isTemplate ? true : (!isConstructor && Cppyy::ExistsMethodTemplate(scope, mtCppName)); + bool storeOnTemplate = isTemplate; if (storeOnTemplate) { sync_templates(pyclass, mtCppName, mtName); // continue processing to actually add the method so that the proxy can find @@ -241,7 +243,7 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons else if (isConstructor) { // ctor mtName = "__init__"; hasConstructor = true; - if (!isAbstract) { + if (!Cppyy::IsAbstract(scope)) { if (flags & CPPScope::kIsMultiCross) { pycall = new CPPMultiConstructor(scope, method); } else @@ -292,14 +294,16 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons } } + // add proxies for un-instantiated/non-overloaded templated methods - const Cppyy::TCppIndex_t nTemplMethods = isNamespace ? 0 : Cppyy::GetNumTemplatedMethods(scope); - for (Cppyy::TCppIndex_t imeth = 0; imeth < nTemplMethods; ++imeth) { - const std::string mtCppName = Cppyy::GetTemplatedMethodName(scope, imeth); + std::vector templ_methods; + Cppyy::GetTemplatedMethods(scope, templ_methods); + for (auto &method : templ_methods) { + const std::string mtCppName = Cppyy::GetMethodName(method); // the number of arguments isn't known until instantiation and as far as C++ is concerned, all // same-named operators are simply overloads; so will pre-emptively add both names if with and // without arguments differ, letting the normal overload mechanism resolve on call - bool isConstructor = Cppyy::IsTemplatedConstructor(scope, imeth); + bool isConstructor = Cppyy::IsConstructor(method); // first add with no arguments std::string mtName0 = isConstructor ? "__init__" : Utility::MapOperatorName(mtCppName, false); @@ -316,16 +320,19 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons // add a pseudo-default ctor, if none defined if (!hasConstructor) { PyCallable* defctor = nullptr; - if (isAbstract) - defctor = new CPPAbstractClassConstructor(scope, (Cppyy::TCppMethod_t)0); - else if (isNamespace) - defctor = new CPPNamespaceConstructor(scope, (Cppyy::TCppMethod_t)0); - else if (!Cppyy::IsComplete(Cppyy::GetScopedFinalName(scope))) { + if (!isComplete) { ((CPPScope*)pyclass)->fFlags |= CPPScope::kIsInComplete; defctor = new CPPIncompleteClassConstructor(scope, (Cppyy::TCppMethod_t)0); - } else + } else if (Cppyy::IsAbstract(scope)) { + defctor = new CPPAbstractClassConstructor(scope, (Cppyy::TCppMethod_t)0); + } else if (isNamespace) { + defctor = new CPPNamespaceConstructor(scope, (Cppyy::TCppMethod_t)0); + } else { defctor = new CPPAllPrivateClassConstructor(scope, (Cppyy::TCppMethod_t)0); - cache["__init__"].push_back(defctor); + } + + if (defctor) + cache["__init__"].push_back(defctor); } // map __call__ to __getitem__ if also mapped to __setitem__ @@ -363,22 +370,19 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons Py_DECREF(dct); // collect data members (including enums) - const Cppyy::TCppIndex_t nDataMembers = Cppyy::GetNumDatamembers(scope); - for (Cppyy::TCppIndex_t idata = 0; idata < nDataMembers; ++idata) { + std::vector datamembers; + Cppyy::GetDatamembers(scope, datamembers); + for (auto &datamember : datamembers) { // allow only public members - if (!Cppyy::IsPublicData(scope, idata)) + if (!Cppyy::IsPublicData(datamember)) continue; // enum datamembers (this in conjunction with previously collected enums above) - if (Cppyy::IsEnumData(scope, idata) && Cppyy::IsStaticData(scope, idata)) { - // some implementation-specific data members have no address: ignore them - if (!Cppyy::GetDatamemberOffset(scope, idata)) - continue; - + if (Cppyy::IsEnumType(Cppyy::GetDatamemberType(datamember)) && Cppyy::IsEnumConstant(datamember)) { // two options: this is a static variable, or it is the enum value, the latter // already exists, so check for it and move on if set PyObject* eset = PyObject_GetAttrString(pyclass, - const_cast(Cppyy::GetDatamemberName(scope, idata).c_str())); + const_cast(Cppyy::GetFinalName(datamember).c_str())); if (eset) { Py_DECREF(eset); continue; @@ -388,15 +392,15 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons // it could still be that this is an anonymous enum, which is not in the list // provided by the class - if (strstr(Cppyy::GetDatamemberType(scope, idata).c_str(), "(anonymous)") != 0 || - strstr(Cppyy::GetDatamemberType(scope, idata).c_str(), "(unnamed)") != 0) { - AddPropertyToClass(pyclass, scope, idata); + if (strstr(Cppyy::GetDatamemberTypeAsString(datamember).c_str(), "(anonymous)") != 0 || + strstr(Cppyy::GetDatamemberTypeAsString(datamember).c_str(), "(unnamed)") != 0) { + AddPropertyToClass(pyclass, scope, datamember); continue; } } // properties (aka public (static) data members) - AddPropertyToClass(pyclass, scope, idata); + AddPropertyToClass(pyclass, scope, datamember); } // restore custom __getattr__ @@ -407,26 +411,24 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons } //---------------------------------------------------------------------------- -static void CollectUniqueBases(Cppyy::TCppType_t klass, std::deque& uqb) +static void CollectUniqueBases(Cppyy::TCppScope_t klass, std::deque& uqb) { // collect bases in acceptable mro order, while removing duplicates (this may // break the overload resolution in esoteric cases, but otherwise the class can // not be used at all, as CPython will refuse the mro). size_t nbases = Cppyy::GetNumBases(klass); - std::deque bids; for (size_t ibase = 0; ibase < nbases; ++ibase) { - const std::string& name = Cppyy::GetBaseName(klass, ibase); + Cppyy::TCppScope_t tp = Cppyy::GetBaseScope(klass, ibase); int decision = 2; - Cppyy::TCppType_t tp = Cppyy::GetScope(name); if (!tp) continue; // means this base with not be available Python-side for (size_t ibase2 = 0; ibase2 < uqb.size(); ++ibase2) { - if (uqb[ibase2] == name) { // not unique ... skip + if (uqb[ibase2] == tp) { // not unique ... skip decision = 0; break; } - if (Cppyy::IsSubtype(tp, bids[ibase2])) { + if (Cppyy::IsSubclass(tp, uqb[ibase2])) { // mro requirement: sub-type has to follow base decision = 1; break; @@ -434,20 +436,18 @@ static void CollectUniqueBases(Cppyy::TCppType_t klass, std::deque& } if (decision == 1) { - uqb.push_front(name); - bids.push_front(tp); + uqb.push_front(tp); } else if (decision == 2) { - uqb.push_back(name); - bids.push_back(tp); + uqb.push_back(tp); } // skipped if decision == 0 (not unique) } } -static PyObject* BuildCppClassBases(Cppyy::TCppType_t klass) +static PyObject* BuildCppClassBases(Cppyy::TCppScope_t klass) { // Build a tuple of python proxy classes of all the bases of the given 'klass'. - std::deque uqb; + std::deque uqb; CollectUniqueBases(klass, uqb); // allocate a tuple for the base classes, special case for first base @@ -462,7 +462,7 @@ static PyObject* BuildCppClassBases(Cppyy::TCppType_t klass) Py_INCREF((PyObject*)(void*)&CPPInstance_Type); PyTuple_SET_ITEM(pybases, 0, (PyObject*)(void*)&CPPInstance_Type); } else { - for (std::deque::size_type ibase = 0; ibase < nbases; ++ibase) { + for (std::deque::size_type ibase = 0; ibase < nbases; ++ibase) { PyObject* pyclass = CreateScopeProxy(uqb[ibase]); if (!pyclass) { Py_DECREF(pybases); @@ -506,16 +506,27 @@ PyObject* CPyCppyy::GetScopeProxy(Cppyy::TCppScope_t scope) return nullptr; } - -//---------------------------------------------------------------------------- -PyObject* CPyCppyy::CreateScopeProxy(Cppyy::TCppScope_t scope, const unsigned flags) -{ -// Convenience function with a lookup first through the known existing proxies. - PyObject* pyclass = GetScopeProxy(scope); - if (pyclass) - return pyclass; - - return CreateScopeProxy(Cppyy::GetScopedFinalName(scope), nullptr, flags); +namespace CPyCppyy { +PyObject *CppType_To_PyObject(Cppyy::TCppType_t type, std::string name, Cppyy::TCppScope_t parent_scope, PyObject *parent) { + Cppyy::TCppType_t resolved_type = Cppyy::ResolveType(type); + if (gPyTypeMap) { + const std::string& resolved = Cppyy::GetTypeAsString(resolved_type); + PyObject* tc = PyDict_GetItemString(gPyTypeMap, resolved.c_str()); // borrowed + if (tc && PyCallable_Check(tc)) { + const std::string& scName = Cppyy::GetScopedFinalName(parent_scope); + PyObject* nt = PyObject_CallFunction(tc, (char*)"ss", name.c_str(), scName != "" ? scName.c_str() : ""); + if (nt) { + if (parent) { + AddScopeToParent(parent, name, nt); + Py_DECREF(parent); + } + return nt; + } + PyErr_Clear(); + } + } + return nullptr; +} } //---------------------------------------------------------------------------- @@ -535,10 +546,10 @@ PyObject* CPyCppyy::CreateScopeProxy(const std::string& name, PyObject* parent, // Build a python shadow class for the named C++ class or namespace. // determine complete scope name, if a python parent has been given - std::string scName = ""; + Cppyy::TCppScope_t parent_scope = 0; if (parent) { if (CPPScope_Check(parent)) - scName = Cppyy::GetScopedFinalName(((CPPScope*)parent)->fCppType); + parent_scope = ((CPPScope*)parent)->fCppType; else { PyObject* parname = PyObject_GetAttr(parent, PyStrings::gName); if (!parname) { @@ -547,66 +558,107 @@ PyObject* CPyCppyy::CreateScopeProxy(const std::string& name, PyObject* parent, } // should be a string - scName = CPyCppyy_PyText_AsString(parname); + std::string scName = CPyCppyy_PyText_AsString(parname); Py_DECREF(parname); if (PyErr_Occurred()) return nullptr; + parent_scope = Cppyy::GetScope(scName); } - // accept this parent scope and use it's name for prefixing Py_INCREF(parent); } // retrieve C++ class (this verifies name, and is therefore done first) - const std::string& lookup = scName.empty() ? name : (scName+"::"+name); - Cppyy::TCppScope_t klass = Cppyy::GetScope(lookup); + if (name == "") { + Cppyy::TCppScope_t klass = Cppyy::GetGlobalScope(); + Py_INCREF(gThisModule); + parent = gThisModule; + return CreateScopeProxy(klass, parent, flags); + } else if (Cppyy::TCppScope_t klass = Cppyy::GetScope(name, parent_scope)) { + if (Cppyy::IsTypedefed(klass) && + Cppyy::IsPointerType(Cppyy::GetTypeFromScope(klass)) && + Cppyy::IsClass(Cppyy::GetUnderlyingScope(klass))) + return nullptr; // this is handled by the caller; typedef to class pointer + return CreateScopeProxy(klass, parent, flags); + } else if (Cppyy::IsBuiltin(name)) { + Cppyy::TCppType_t type = Cppyy::GetType(name); + PyObject *result = nullptr; + if (type) + result = CppType_To_PyObject(type, name, parent_scope, parent); + if (result) + return result; + } + // all options have been exhausted: it doesn't exist as such + PyErr_Format(PyExc_TypeError, "\'%s\' is not a known C++ class", name.c_str()); + Py_XDECREF(parent); + return nullptr; +} + +//---------------------------------------------------------------------------- +PyObject* CPyCppyy::CreateScopeProxy(Cppyy::TCppScope_t scope, PyObject* parent, const unsigned flags) +{ +// Convenience function with a lookup first through the known existing proxies. + PyObject* pyclass = GetScopeProxy(scope); + if (pyclass) + return pyclass; - if (!(bool)klass && Cppyy::IsTemplate(lookup)) { - // a "naked" templated class is requested: return callable proxy for instantiations + Cppyy::TCppScope_t parent_scope = nullptr; + if (CPPScope_Check(parent)) + parent_scope = ((CPPScope*)parent)->fCppType; + if (!parent_scope) + parent_scope = Cppyy::GetParentScope(scope); + + if (!parent) { + if (parent_scope) + parent = CreateScopeProxy(parent_scope); + else { + Py_INCREF(gThisModule); + parent = gThisModule; + } + } + + std::string name = Cppyy::GetFinalName(scope); + bool is_typedef = false; + std::string typedefed_name = ""; + if (Cppyy::IsTypedefed(scope)) { + is_typedef = true; + typedefed_name = Cppyy::GetMethodFullName(scope); + Cppyy::TCppScope_t underlying_scope = Cppyy::GetUnderlyingScope(scope); + if ((underlying_scope) && (underlying_scope != scope)) { + scope = underlying_scope; + } else { + if (PyObject *result = CppType_To_PyObject(Cppyy::GetTypeFromScope(scope), name, parent_scope, parent)) + return result; + + PyErr_Format(PyExc_TypeError, "\'%s\' is not a known C++ class", Cppyy::GetScopedFinalName(scope).c_str()); + Py_XDECREF(parent); + return nullptr; + } + } + + if (Cppyy::IsTemplate(scope)) { + // a "naked" templated class is requested: return callable proxy for instantiations PyObject* pytcl = PyObject_GetAttr(gThisModule, PyStrings::gTemplate); + PyObject* cppscope = PyLong_FromVoidPtr(scope); PyObject* pytemplate = PyObject_CallFunction( - pytcl, const_cast("s"), const_cast(lookup.c_str())); + pytcl, const_cast("sO"), + const_cast(Cppyy::GetScopedFinalName(scope).c_str()), + cppscope); Py_DECREF(pytcl); // cache the result - AddScopeToParent(parent ? parent : gThisModule, name, pytemplate); + AddScopeToParent(parent, name, pytemplate); // done, next step should be a call into this template Py_XDECREF(parent); return pytemplate; } - if (!(bool)klass) { - // could be an enum, which are treated separately in CPPScope (TODO: maybe they - // should be handled here instead anyway??) - if (Cppyy::IsEnum(lookup)) - return nullptr; - - // final possibility is a typedef of a builtin; these are mapped on the python side - std::string resolved = Cppyy::ResolveName(lookup); - if (gPyTypeMap) { - PyObject* tc = PyDict_GetItemString(gPyTypeMap, resolved.c_str()); // borrowed - if (tc && PyCallable_Check(tc)) { - PyObject* nt = PyObject_CallFunction(tc, (char*)"ss", name.c_str(), scName.c_str()); - if (nt) { - if (parent) { - AddScopeToParent(parent, name, nt); - Py_DECREF(parent); - } - return nt; - } - PyErr_Clear(); - } - } - - // all options have been exhausted: it doesn't exist as such - PyErr_Format(PyExc_TypeError, "\'%s\' is not a known C++ class", lookup.c_str()); - Py_XDECREF(parent); + if (Cppyy::IsEnumScope(scope)) return nullptr; - } // locate class by ID, if possible, to prevent parsing scopes/templates anew - PyObject* pyscope = GetScopeProxy(klass); + PyObject* pyscope = GetScopeProxy(scope); if (pyscope) { if (parent) { AddScopeToParent(parent, name, pyscope); @@ -615,86 +667,20 @@ PyObject* CPyCppyy::CreateScopeProxy(const std::string& name, PyObject* parent, return pyscope; } -// now have a class ... get the actual, fully scoped class name, so that typedef'ed -// classes are created in the right place - const std::string& actual = Cppyy::GetScopedFinalName(klass); - if (actual != lookup) { - pyscope = CreateScopeProxy(actual); - if (!pyscope) PyErr_Clear(); - } - -// locate the parent, if necessary, for memoizing the class if not specified - std::string::size_type last = 0; - if (!parent) { - // TODO: move this to TypeManip, which already has something similar in - // the form of 'extract_namespace' - // need to deal with template parameters that can have scopes themselves - int tpl_open = 0; - for (std::string::size_type pos = 0; pos < name.size(); ++pos) { - std::string::value_type c = name[pos]; - - // count '<' and '>' to be able to skip template contents - if (c == '<') - ++tpl_open; - else if (c == '>') - --tpl_open; - - // by only checking for "::" the last part (class name) is dropped - else if (tpl_open == 0 && \ - c == ':' && pos+1 < name.size() && name[ pos+1 ] == ':') { - // found a new scope part - const std::string& part = name.substr(last, pos-last); - - PyObject* next = PyObject_GetAttrString( - parent ? parent : gThisModule, const_cast(part.c_str())); - - if (!next) { // lookup failed, try to create it - PyErr_Clear(); - next = CreateScopeProxy(part, parent); - } - Py_XDECREF(parent); - - if (!next) // create failed, give up - return nullptr; - - // found scope part - parent = next; - - // done with part (note that pos is moved one ahead here) - last = pos+2; ++pos; - } - } - - if (parent && !CPPScope_Check(parent)) { - // Special case: parent found is not one of ours (it's e.g. a pure Python module), so - // continuing would fail badly. One final lookup, then out of here ... - std::string unscoped = name.substr(last, std::string::npos); - PyObject* ret = PyObject_GetAttrString(parent, unscoped.c_str()); - Py_DECREF(parent); - return ret; - } - } - -// use the module as a fake scope if no outer scope found - if (!parent) { - Py_INCREF(gThisModule); - parent = gThisModule; - } - -// if the scope was earlier found as actual, then we're done already, otherwise -// build a new scope proxy + // if the scope was earlier found as actual, then we're done already, otherwise + // build a new scope proxy if (!pyscope) { // construct the base classes - PyObject* pybases = BuildCppClassBases(klass); + PyObject* pybases = BuildCppClassBases(scope); if (pybases != 0) { // create a fresh Python class, given bases, name, and empty dictionary - pyscope = CreateNewCppProxyClass(klass, pybases); + pyscope = CreateNewCppProxyClass(scope, pybases); Py_DECREF(pybases); } // fill the dictionary, if successful if (pyscope) { - if (BuildScopeProxyDict(klass, pyscope, flags)) { + if (BuildScopeProxyDict(scope, pyscope, flags)) { // something failed in building the dictionary Py_DECREF(pyscope); pyscope = nullptr; @@ -703,11 +689,11 @@ PyObject* CPyCppyy::CreateScopeProxy(const std::string& name, PyObject* parent, // store a ref from cppyy scope id to new python class if (pyscope && !(((CPPScope*)pyscope)->fFlags & CPPScope::kIsInComplete)) { - gPyClasses[klass] = PyWeakref_NewRef(pyscope, nullptr); + gPyClasses[scope] = PyWeakref_NewRef(pyscope, nullptr); if (!(((CPPScope*)pyscope)->fFlags & CPPScope::kIsNamespace)) { // add python-style features to classes only - if (!Pythonize(pyscope, Cppyy::GetScopedFinalName(klass))) { + if (!Pythonize(pyscope, scope)) { Py_DECREF(pyscope); pyscope = nullptr; } @@ -725,8 +711,14 @@ PyObject* CPyCppyy::CreateScopeProxy(const std::string& name, PyObject* parent, } // store on parent if found/created and complete - if (pyscope && !(((CPPScope*)pyscope)->fFlags & CPPScope::kIsInComplete)) + if (pyscope && !(((CPPScope*)pyscope)->fFlags & CPPScope::kIsInComplete)) { + // FIXME: This is to mimic original behaviour. Still required? + if (Cppyy::IsTemplateInstantiation(scope)) + name = Cppyy::GetScopedFinalName(scope); + if (is_typedef && !typedefed_name.empty()) + AddScopeToParent(parent, typedefed_name, pyscope); AddScopeToParent(parent, name, pyscope); + } Py_DECREF(parent); // all done @@ -743,7 +735,7 @@ PyObject* CPyCppyy::CreateExcScopeProxy(PyObject* pyscope, PyObject* pyname, PyO // derives from Pythons Exception class // start with creation of CPPExcInstance type base classes - std::deque uqb; + std::deque uqb; CollectUniqueBases(((CPPScope*)pyscope)->fCppType, uqb); size_t nbases = uqb.size(); @@ -765,19 +757,18 @@ PyObject* CPyCppyy::CreateExcScopeProxy(PyObject* pyscope, PyObject* pyname, PyO } else { PyObject* best_base = nullptr; - for (std::deque::size_type ibase = 0; ibase < nbases; ++ibase) { + for (std::deque::size_type ibase = 0; ibase < nbases; ++ibase) { // retrieve bases through their enclosing scope to guarantee treatment as // exception classes and proper caching - const std::string& finalname = Cppyy::GetScopedFinalName(Cppyy::GetScope(uqb[ibase])); - const std::string& parentname = TypeManip::extract_namespace(finalname); - PyObject* base_parent = CreateScopeProxy(parentname); + Cppyy::TCppScope_t parent_scope = Cppyy::GetParentScope(uqb[ibase]); + PyObject* base_parent = CreateScopeProxy(parent_scope); if (!base_parent) { Py_DECREF(pybases); return nullptr; } PyObject* excbase = PyObject_GetAttrString(base_parent, - parentname.empty() ? finalname.c_str() : finalname.substr(parentname.size()+2, std::string::npos).c_str()); + Cppyy::GetFinalName(uqb[ibase]).c_str()); Py_DECREF(base_parent); if (!excbase) { Py_DECREF(pybases); @@ -787,7 +778,8 @@ PyObject* CPyCppyy::CreateExcScopeProxy(PyObject* pyscope, PyObject* pyname, PyO if (PyType_IsSubtype((PyTypeObject*)excbase, &CPPExcInstance_Type)) { Py_XDECREF(best_base); best_base = excbase; - if (finalname != "std::exception") + + if (Cppyy::GetScopedFinalName(uqb[ibase]) != "std::exception") break; } else { // just skip: there will be at least one exception derived base class @@ -821,7 +813,7 @@ PyObject* CPyCppyy::CreateExcScopeProxy(PyObject* pyscope, PyObject* pyname, PyO //---------------------------------------------------------------------------- PyObject* CPyCppyy::BindCppObjectNoCast(Cppyy::TCppObject_t address, - Cppyy::TCppType_t klass, const unsigned flags) + Cppyy::TCppScope_t klass, const unsigned flags) { // only known or knowable objects will be bound (null object is ok) if (!klass) { @@ -908,7 +900,7 @@ PyObject* CPyCppyy::BindCppObjectNoCast(Cppyy::TCppObject_t address, //---------------------------------------------------------------------------- PyObject* CPyCppyy::BindCppObject(Cppyy::TCppObject_t address, - Cppyy::TCppType_t klass, const unsigned flags) + Cppyy::TCppScope_t klass, const unsigned flags) { // if the object is a null pointer, return a typed one (as needed for overloading) if (!address) @@ -927,24 +919,19 @@ PyObject* CPyCppyy::BindCppObject(Cppyy::TCppObject_t address, // successful, no down-casting is attempted? // TODO: optimize for final classes unsigned new_flags = flags; - if (gPinnedTypes.empty() || gPinnedTypes.find(klass) == gPinnedTypes.end()) { - if (!isRef) { - Cppyy::TCppType_t clActual = Cppyy::GetActualClass(klass, address); - - if (clActual) { - if (clActual != klass) { - intptr_t offset = Cppyy::GetBaseOffset( - clActual, klass, address, -1 /* down-cast */, true /* report errors */); - if (offset != -1) { // may fail if clActual not fully defined - address = (void*)((intptr_t)address + offset); - klass = clActual; - } + if (!isRef && (gPinnedTypes.empty() || gPinnedTypes.find(klass) == gPinnedTypes.end())) { + Cppyy::TCppType_t clActual = Cppyy::GetActualClass(klass, address); + + if (clActual) { + if (clActual != klass) { + intptr_t offset = Cppyy::GetBaseOffset( + clActual, klass, address, -1 /* down-cast */, true /* report errors */); + if (offset != -1) { // may fail if clActual not fully defined + address = (void*)((intptr_t)address + offset); + klass = clActual; } - new_flags |= CPPInstance::kIsActual; } - } else { - Cppyy::TCppType_t clActual = Cppyy::GetActualClass(klass, *(void**)address); - klass = clActual; + new_flags |= CPPInstance::kIsActual; } } @@ -954,7 +941,7 @@ PyObject* CPyCppyy::BindCppObject(Cppyy::TCppObject_t address, //---------------------------------------------------------------------------- PyObject* CPyCppyy::BindCppObjectArray( - Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, cdims_t dims) + Cppyy::TCppObject_t address, Cppyy::TCppScope_t klass, cdims_t dims) { // TODO: this function exists for symmetry; need to figure out if it's useful return TupleOfInstances_New(address, klass, dims); diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.h b/bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.h index cfa4360294d08..7a074f8db98ef 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/ProxyWrappers.h @@ -12,21 +12,21 @@ namespace CPyCppyy { // construct a Python shadow class for the named C++ class PyObject* GetScopeProxy(Cppyy::TCppScope_t); -PyObject* CreateScopeProxy(Cppyy::TCppScope_t, const unsigned flags = 0); PyObject* CreateScopeProxy(PyObject*, PyObject* args); PyObject* CreateScopeProxy( const std::string& scope_name, PyObject* parent = nullptr, const unsigned flags = 0); +PyObject* CreateScopeProxy(Cppyy::TCppScope_t scope, PyObject* parent = nullptr, const unsigned flags = 0); // C++ exceptions form a special case b/c they have to derive from BaseException PyObject* CreateExcScopeProxy(PyObject* pyscope, PyObject* pyname, PyObject* parent); // bind a C++ object into a Python proxy object (flags are CPPInstance::Default) PyObject* BindCppObjectNoCast(Cppyy::TCppObject_t object, - Cppyy::TCppType_t klass, const unsigned flags = 0); + Cppyy::TCppScope_t klass, const unsigned flags = 0); PyObject* BindCppObject(Cppyy::TCppObject_t object, - Cppyy::TCppType_t klass, const unsigned flags = 0); + Cppyy::TCppScope_t klass, const unsigned flags = 0); PyObject* BindCppObjectArray( - Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, cdims_t dims); + Cppyy::TCppObject_t address, Cppyy::TCppScope_t klass, cdims_t dims); } // namespace CPyCppyy diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/PyCallable.h b/bindings/pyroot/cppyy/CPyCppyy/src/PyCallable.h index d2a072c5b389e..62ce3aa1af693 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/PyCallable.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/PyCallable.h @@ -18,8 +18,6 @@ class PyCallable { public: virtual PyObject* GetSignature(bool show_formalargs = true) = 0; - virtual PyObject* GetSignatureNames() = 0; - virtual PyObject* GetSignatureTypes() = 0; virtual PyObject* GetPrototype(bool show_formalargs = true) = 0; virtual PyObject* GetTypeName() { return GetPrototype(false); } virtual PyObject* GetDocString() { return GetPrototype(); } diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/PyException.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/PyException.cxx index f478ab33f98f4..2929683fe0667 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/PyException.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/PyException.cxx @@ -28,17 +28,8 @@ CPyCppyy::PyException::PyException() PyGILState_STATE state = PyGILState_Ensure(); #endif -#if PY_VERSION_HEX >= 0x030c0000 - PyObject *pyvalue = PyErr_GetRaisedException(); - PyObject *pytype = pyvalue ? (PyObject *)Py_TYPE(pyvalue) : nullptr; - PyObject* traceback = pyvalue ? PyException_GetTraceback(pyvalue) : nullptr; -#else PyObject* pytype = nullptr, *pyvalue = nullptr, *pytrace = nullptr; PyErr_Fetch(&pytype, &pyvalue, &pytrace); - PyObject* traceback = pytrace; // to keep the original unchanged - Py_XINCREF(traceback); -#endif - if (pytype && pyvalue) { const char* tname = PyExceptionClass_Name(pytype); if (tname) { @@ -55,6 +46,9 @@ CPyCppyy::PyException::PyException() } } + PyObject* traceback = pytrace; // to keep the original unchanged + Py_XINCREF(traceback); + std::string locName; std::string locFile; int locLine = 0; @@ -94,11 +88,7 @@ CPyCppyy::PyException::PyException() Py_XDECREF(traceback); -#if PY_VERSION_HEX >= 0x030c0000 - PyErr_SetRaisedException(pyvalue); -#else PyErr_Restore(pytype, pyvalue, pytrace); -#endif if (fMsg.empty()) fMsg = "python exception"; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/PyStrings.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/PyStrings.cxx index abbf16ece0049..714f51151f642 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/PyStrings.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/PyStrings.cxx @@ -59,6 +59,7 @@ PyObject* CPyCppyy::PyStrings::gTemplate = nullptr; PyObject* CPyCppyy::PyStrings::gVectorAt = nullptr; PyObject* CPyCppyy::PyStrings::gInsert = nullptr; PyObject* CPyCppyy::PyStrings::gValueType = nullptr; +PyObject* CPyCppyy::PyStrings::gValueTypePtr = nullptr; PyObject* CPyCppyy::PyStrings::gValueSize = nullptr; PyObject* CPyCppyy::PyStrings::gCppReal = nullptr; @@ -145,6 +146,7 @@ bool CPyCppyy::CreatePyStrings() { CPPYY_INITIALIZE_STRING(gVectorAt, _vector__at); CPPYY_INITIALIZE_STRING(gInsert, insert); CPPYY_INITIALIZE_STRING(gValueType, value_type); + CPPYY_INITIALIZE_STRING(gValueTypePtr, _value_type); CPPYY_INITIALIZE_STRING(gValueSize, value_size); CPPYY_INITIALIZE_STRING(gCppReal, __cpp_real); @@ -218,6 +220,7 @@ PyObject* CPyCppyy::DestroyPyStrings() { Py_DECREF(PyStrings::gVectorAt); PyStrings::gVectorAt = nullptr; Py_DECREF(PyStrings::gInsert); PyStrings::gInsert = nullptr; Py_DECREF(PyStrings::gValueType); PyStrings::gValueType = nullptr; + Py_DECREF(PyStrings::gValueTypePtr);PyStrings::gValueTypePtr= nullptr; Py_DECREF(PyStrings::gValueSize); PyStrings::gValueSize = nullptr; Py_DECREF(PyStrings::gCppReal); PyStrings::gCppReal = nullptr; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/PyStrings.h b/bindings/pyroot/cppyy/CPyCppyy/src/PyStrings.h index 55eaef58273a3..ec9043cbe5f57 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/PyStrings.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/PyStrings.h @@ -62,6 +62,7 @@ namespace PyStrings { extern PyObject* gVectorAt; extern PyObject* gInsert; extern PyObject* gValueType; + extern PyObject* gValueTypePtr; extern PyObject* gValueSize; extern PyObject* gCppReal; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Pythonize.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/Pythonize.cxx index 70d0b22aa8c49..20c12ff709b14 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Pythonize.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Pythonize.cxx @@ -5,6 +5,7 @@ #include "CPPInstance.h" #include "CPPFunction.h" #include "CPPOverload.h" +#include "Cppyy.h" #include "CustomPyTypes.h" #include "LowLevelViews.h" #include "ProxyWrappers.h" @@ -67,7 +68,7 @@ PyObject* GetAttrDirect(PyObject* pyclass, PyObject* pyname) { inline bool IsTemplatedSTLClass(const std::string& name, const std::string& klass) { // Scan the name of the class and determine whether it is a template instantiation. auto pos = name.find(klass); - return (pos == 0 || pos == 5) && name.find("::", name.rfind(">")) == std::string::npos; + return pos == 5 && name.rfind("std::", 0, 5) == 0 && name.find("::", name.rfind(">")) == std::string::npos; } // to prevent compiler warnings about const char* -> char* @@ -187,7 +188,6 @@ PyObject* DeRefGetAttr(PyObject* self, PyObject* name) return nullptr; } - return smart_follow(self, name, PyStrings::gDeref); } @@ -336,11 +336,11 @@ static bool FillVector(PyObject* vecin, PyObject* args, ItemGetter* getter) if (fi && (PyTuple_CheckExact(fi) || PyList_CheckExact(fi))) { // use emplace_back to construct the vector entries one by one PyObject* eb_call = PyObject_GetAttrString(vecin, (char*)"emplace_back"); - PyObject* vtype = GetAttrDirect((PyObject*)Py_TYPE(vecin), PyStrings::gValueType); + PyObject* vtype = GetAttrDirect((PyObject*)Py_TYPE(vecin), PyStrings::gValueTypePtr); bool value_is_vector = false; - if (vtype && CPyCppyy_PyText_Check(vtype)) { + if (vtype && PyLong_Check(vtype)) { // if the value_type is a vector, then allow for initialization from sequences - if (std::string(CPyCppyy_PyText_AsString(vtype)).rfind("std::vector", 0) != std::string::npos) + if (Cppyy::GetTypeAsString(PyLong_AsVoidPtr(vtype)).rfind("std::vector", 0) != std::string::npos) value_is_vector = true; } else PyErr_Clear(); @@ -445,20 +445,9 @@ PyObject* VectorIAdd(PyObject* self, PyObject* args, PyObject* /* kwds */) if (PyObject_CheckBuffer(fi) && !(CPyCppyy_PyText_Check(fi) || PyBytes_Check(fi))) { PyObject* vend = PyObject_CallMethodNoArgs(self, PyStrings::gEnd); if (vend) { - // when __iadd__ is overriden, the operation does not end with - // calling the __iadd__ method, but also assigns the result to the - // lhs of the iadd. For example, performing vec += arr, Python - // first calls our override, and then does vec = vec.iadd(arr). - PyObject *it = PyObject_CallMethodObjArgs(self, PyStrings::gInsert, vend, fi, nullptr); + PyObject* result = PyObject_CallMethodObjArgs(self, PyStrings::gInsert, vend, fi, nullptr); Py_DECREF(vend); - - if (!it) - return nullptr; - - Py_DECREF(it); - // Assign the result of the __iadd__ override to the std::vector - Py_INCREF(self); - return self; + return result; } } } @@ -535,13 +524,6 @@ PyObject* VectorData(PyObject* self, PyObject*) } -// This function implements __array__, added to std::vector python proxies and causes -// a bug (see explanation at Utility::AddToClass(pyclass, "__array__"...) in CPyCppyy::Pythonize) -// The recursive nature of this function, passes each subarray (pydata) to the next call and only -// the final buffer is cast to a lowlevel view and resized (in VectorData), resulting in only the -// first 1D array to be returned. See https://github.com/root-project/root/issues/17729 -// It is temporarily removed to prevent errors due to -Wunused-function, since it is no longer added. -#if 0 //--------------------------------------------------------------------------- PyObject* VectorArray(PyObject* self, PyObject* args, PyObject* kwargs) { @@ -552,27 +534,22 @@ PyObject* VectorArray(PyObject* self, PyObject* args, PyObject* kwargs) Py_DECREF(pydata); return newarr; } -#endif + //----------------------------------------------------------------------------- static PyObject* vector_iter(PyObject* v) { vectoriterobject* vi = PyObject_GC_New(vectoriterobject, &VectorIter_Type); if (!vi) return nullptr; + Py_INCREF(v); vi->ii_container = v; // tell the iterator code to set a life line if this container is a temporary vi->vi_flags = vectoriterobject::kDefault; -#if PY_VERSION_HEX >= 0x030e0000 - if (PyUnstable_Object_IsUniqueReferencedTemporary(v) || (((CPPInstance*)v)->fFlags & CPPInstance::kIsValue)) -#else - if (Py_REFCNT(v) <= 1 || (((CPPInstance*)v)->fFlags & CPPInstance::kIsValue)) -#endif + if (v->ob_refcnt <= 2 || (((CPPInstance*)v)->fFlags & CPPInstance::kIsValue)) vi->vi_flags = vectoriterobject::kNeedLifeLine; - Py_INCREF(v); - - PyObject* pyvalue_type = PyObject_GetAttr((PyObject*)Py_TYPE(v), PyStrings::gValueType); + PyObject* pyvalue_type = PyObject_GetAttr((PyObject*)Py_TYPE(v), PyStrings::gValueTypePtr); if (pyvalue_type) { PyObject* pyvalue_size = GetAttrDirect((PyObject*)Py_TYPE(v), PyStrings::gValueSize); if (pyvalue_size) { @@ -583,29 +560,32 @@ static PyObject* vector_iter(PyObject* v) { vi->vi_stride = 0; } - if (CPyCppyy_PyText_Check(pyvalue_type)) { - std::string value_type = CPyCppyy_PyText_AsString(pyvalue_type); - value_type = Cppyy::ResolveName(value_type); - vi->vi_klass = Cppyy::GetScope(value_type); + if (PyLong_Check(pyvalue_type)) { + Cppyy::TCppType_t value_type = PyLong_AsVoidPtr(pyvalue_type); + value_type = Cppyy::ResolveType(value_type); + vi->vi_klass = Cppyy::GetScopeFromType(value_type); if (!vi->vi_klass) { // look for a special case of pointer to a class type (which is a builtin, but it // is more useful to treat it polymorphically by allowing auto-downcasts) - const std::string& clean_type = TypeManip::clean_type(value_type, false, false); + const std::string& clean_type = TypeManip::clean_type(Cppyy::GetTypeAsString(value_type), false, false); Cppyy::TCppScope_t c = Cppyy::GetScope(clean_type); - if (c && TypeManip::compound(value_type) == "*") { + if (c && TypeManip::compound(Cppyy::GetTypeAsString(value_type)) == "*") { vi->vi_klass = c; vi->vi_flags = vectoriterobject::kIsPolymorphic; } } + if (Cppyy::IsPointerType(value_type)) + vi->vi_flags = vectoriterobject::kIsPolymorphic; if (vi->vi_klass) { vi->vi_converter = nullptr; if (!vi->vi_flags) { - if (value_type.back() != '*') // meaning, object stored by-value + value_type = Cppyy::ResolveType(value_type); + if (Cppyy::GetTypeAsString(value_type).back() != '*') // meaning, object stored by-value vi->vi_flags = vectoriterobject::kNeedLifeLine; } } else vi->vi_converter = CPyCppyy::CreateConverter(value_type); - if (!vi->vi_stride) vi->vi_stride = Cppyy::SizeOf(value_type); + if (!vi->vi_stride) vi->vi_stride = Cppyy::SizeOfType(value_type); } else if (CPPScope_Check(pyvalue_type)) { vi->vi_klass = ((CPPClass*)pyvalue_type)->fCppType; @@ -1136,15 +1116,15 @@ static int PyObject_Compare(PyObject* one, PyObject* other) { } #endif static inline -PyObject* CPyCppyy_PyString_FromCppString(std::string_view s, bool native=true) { +PyObject* CPyCppyy_PyString_FromCppString(std::string* s, bool native=true) { if (native) - return PyBytes_FromStringAndSize(s.data(), s.size()); - return CPyCppyy_PyText_FromStringAndSize(s.data(), s.size()); + return PyBytes_FromStringAndSize(s->data(), s->size()); + return CPyCppyy_PyText_FromStringAndSize(s->data(), s->size()); } static inline -PyObject* CPyCppyy_PyString_FromCppString(std::wstring_view s, bool native=true) { - PyObject* pyobj = PyUnicode_FromWideChar(s.data(), s.size()); +PyObject* CPyCppyy_PyString_FromCppString(std::wstring* s, bool native=true) { + PyObject* pyobj = PyUnicode_FromWideChar(s->data(), s->size()); if (pyobj && native) { PyObject* pybytes = PyUnicode_AsEncodedString(pyobj, "UTF-8", "strict"); Py_DECREF(pyobj); @@ -1159,7 +1139,7 @@ PyObject* name##StringGetData(PyObject* self, bool native=true) \ { \ if (CPyCppyy::CPPInstance_Check(self)) { \ type* obj = ((type*)((CPPInstance*)self)->GetObject()); \ - if (obj) return CPyCppyy_PyString_FromCppString(*obj, native); \ + if (obj) return CPyCppyy_PyString_FromCppString(obj, native); \ } \ PyErr_Format(PyExc_TypeError, "object mismatch (%s expected)", #type); \ return nullptr; \ @@ -1236,7 +1216,6 @@ PyObject* name##StringCompare(PyObject* self, PyObject* obj) \ CPPYY_IMPL_STRING_PYTHONIZATION_CMP(std::string, STL) CPPYY_IMPL_STRING_PYTHONIZATION_CMP(std::wstring, STLW) -CPPYY_IMPL_STRING_PYTHONIZATION_CMP(std::string_view, STLView) static inline std::string* GetSTLString(CPPInstance* self) { if (!CPPInstance_Check(self)) { @@ -1360,7 +1339,6 @@ PyObject* STLStringGetAttr(CPPInstance* self, PyObject* attr_name) } -#if 0 PyObject* UTF8Repr(PyObject* self) { // force C++ string types conversion to Python str per Python __repr__ requirements @@ -1382,7 +1360,6 @@ PyObject* UTF8Str(PyObject* self) Py_DECREF(res); return str_res; } -#endif Py_hash_t STLStringHash(PyObject* self) { @@ -1616,8 +1593,10 @@ bool run_pythonizors(PyObject* pyclass, PyObject* pyname, const std::vectortp_iter) { if (HasAttrDirect(pyclass, PyStrings::gBegin) && HasAttrDirect(pyclass, PyStrings::gEnd)) { // obtain the name of the return type - const auto& v = Cppyy::GetMethodIndicesFromName(klass->fCppType, "begin"); - if (!v.empty()) { + const auto& methods = Cppyy::GetMethodsFromName(klass->fCppType, "begin"); + if (!methods.empty()) { // check return type; if not explicitly an iterator, add it to the "known" return // types to add the "next" method on use - Cppyy::TCppMethod_t meth = Cppyy::GetMethod(klass->fCppType, v[0]); - const std::string& resname = Cppyy::GetMethodResultType(meth); + Cppyy::TCppMethod_t meth = methods[0]; + const std::string& resname = Cppyy::GetMethodReturnTypeAsString(meth); bool isIterator = gIteratorTypes.find(resname) != gIteratorTypes.end(); if (!isIterator && Cppyy::GetScope(resname)) { if (resname.find("iterator") == std::string::npos) @@ -1701,7 +1680,7 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name) // comparisons to None; if no operator is available, a hook is installed for lazy // lookups in the global and/or class namespace if (HasAttrDirect(pyclass, PyStrings::gEq, true) && \ - Cppyy::GetMethodIndicesFromName(klass->fCppType, "__eq__").empty()) { + Cppyy::GetMethodsFromName(klass->fCppType, "__eq__").empty()) { PyObject* cppol = PyObject_GetAttr(pyclass, PyStrings::gEq); if (!klass->fOperators) klass->fOperators = new Utility::PyOperators(); klass->fOperators->fEq = cppol; @@ -1718,7 +1697,7 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name) } if (HasAttrDirect(pyclass, PyStrings::gNe, true) && \ - Cppyy::GetMethodIndicesFromName(klass->fCppType, "__ne__").empty()) { + Cppyy::GetMethodsFromName(klass->fCppType, "__ne__").empty()) { PyObject* cppol = PyObject_GetAttr(pyclass, PyStrings::gNe); if (!klass->fOperators) klass->fOperators = new Utility::PyOperators(); klass->fOperators->fNe = cppol; @@ -1733,7 +1712,6 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name) PyObject_SetAttr(pyclass, PyStrings::gNe, top_ne); } -#if 0 if (HasAttrDirect(pyclass, PyStrings::gRepr, true)) { // guarantee that the result of __repr__ is a Python string Utility::AddToClass(pyclass, "__cpp_repr", "__repr__"); @@ -1745,14 +1723,13 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name) Utility::AddToClass(pyclass, "__cpp_str", "__str__"); Utility::AddToClass(pyclass, "__str__", (PyCFunction)UTF8Str, METH_NOARGS); } -#endif - if (Cppyy::IsAggregate(((CPPClass*)pyclass)->fCppType) && name.compare(0, 5, "std::", 5) != 0 && - name.compare(0, 6, "tuple<", 6) != 0) { + if (Cppyy::IsAggregate(((CPPClass*)pyclass)->fCppType) && name.compare(0, 5, "std::", 5) != 0) { // create a pseudo-constructor to allow initializer-style object creation Cppyy::TCppType_t kls = ((CPPClass*)pyclass)->fCppType; - Cppyy::TCppIndex_t ndata = Cppyy::GetNumDatamembers(kls); - if (ndata) { + std::vector datamems; + Cppyy::GetDatamembers(kls, datamems); + if (!datamems.empty()) { std::string rname = name; TypeManip::cppscope_to_legalname(rname); @@ -1761,23 +1738,30 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name) << "void init_" << rname << "(" << name << "*& self"; bool codegen_ok = true; std::vector arg_types, arg_names, arg_defaults; + const int ndata = datamems.size(); arg_types.reserve(ndata); arg_names.reserve(ndata); arg_defaults.reserve(ndata); - for (Cppyy::TCppIndex_t i = 0; i < ndata; ++i) { - if (Cppyy::IsStaticData(kls, i) || !Cppyy::IsPublicData(kls, i)) + for (auto data : datamems) { + if (Cppyy::IsStaticDatamember(data) || !Cppyy::IsPublicData(data)) continue; - const std::string& txt = Cppyy::GetDatamemberType(kls, i); - const std::string& res = Cppyy::IsEnum(txt) ? txt : Cppyy::ResolveName(txt); + Cppyy::TCppType_t datammember_type = + Cppyy::GetDatamemberType(data); + const std::string &res = + Cppyy::IsEnumType(datammember_type) + ? Cppyy::GetScopedFinalName( + Cppyy::GetScopeFromType(datammember_type)) + : Cppyy::GetTypeAsString( + Cppyy::ResolveType(datammember_type)); const std::string& cpd = TypeManip::compound(res); std::string res_clean = TypeManip::clean_type(res, false, true); if (res_clean == "internal_enum_type_t") - res_clean = txt; // restore (properly scoped name) + res_clean = res; // restore (properly scoped name) if (res.rfind(']') == std::string::npos && res.rfind(')') == std::string::npos) { if (!cpd.empty()) arg_types.push_back(res_clean+cpd); else arg_types.push_back("const "+res_clean+"&"); - arg_names.push_back(Cppyy::GetDatamemberName(kls, i)); + arg_names.push_back(Cppyy::GetFinalName(data)); if ((!cpd.empty() && cpd.back() == '*') || Cppyy::IsBuiltin(res_clean)) arg_defaults.push_back("0"); else { @@ -1805,10 +1789,10 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name) if (Cppyy::Compile(initdef.str(), true /* silent */)) { Cppyy::TCppScope_t cis = Cppyy::GetScope("__cppyy_internal"); - const auto& mix = Cppyy::GetMethodIndicesFromName(cis, "init_"+rname); - if (mix.size()) { + const auto& methods = Cppyy::GetMethodsFromName(cis, "init_" + rname); + if (methods.size()) { if (!Utility::AddToClass(pyclass, "__init__", - new CPPFunction(cis, Cppyy::GetMethod(cis, mix[0])))) + new CPPFunction(cis, methods[0]))) PyErr_Clear(); } } @@ -1833,18 +1817,10 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name) // data with size Utility::AddToClass(pyclass, "__real_data", "data"); - PyErr_Clear(); // AddToClass might have failed for data Utility::AddToClass(pyclass, "data", (PyCFunction)VectorData); - // The addition of the __array__ utility to std::vector Python proxies causes a - // bug where the resulting array is a single dimension, causing loss of data when - // converting to numpy arrays, for >1dim vectors. Since this C++ pythonization - // was added with the upgrade in 6.32, and is only defined and used recursively, - // the safe option is to disable this function and no longer add it. -#if 0 // numpy array conversion Utility::AddToClass(pyclass, "__array__", (PyCFunction)VectorArray, METH_VARARGS | METH_KEYWORDS /* unused */); -#endif // checked getitem if (HasAttrDirect(pyclass, PyStrings::gLen)) { @@ -1859,14 +1835,18 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name) Utility::AddToClass(pyclass, "__iadd__", (PyCFunction)VectorIAdd, METH_VARARGS | METH_KEYWORDS); // helpers for iteration - const std::string& vtype = Cppyy::ResolveName(name+"::value_type"); - if (vtype.rfind("value_type") == std::string::npos) { // actually resolved? - PyObject* pyvalue_type = CPyCppyy_PyText_FromString(vtype.c_str()); + Cppyy::TCppType_t value_type = Cppyy::GetTypeFromScope(Cppyy::GetNamed("value_type", scope)); + Cppyy::TCppType_t vtype = Cppyy::ResolveType(value_type); + if (vtype) { // actually resolved? + PyObject* pyvalue_type = PyLong_FromVoidPtr(vtype); + PyObject_SetAttr(pyclass, PyStrings::gValueTypePtr, pyvalue_type); + Py_DECREF(pyvalue_type); + pyvalue_type = PyUnicode_FromString(Cppyy::GetTypeAsString(vtype).c_str()); PyObject_SetAttr(pyclass, PyStrings::gValueType, pyvalue_type); Py_DECREF(pyvalue_type); } - size_t typesz = Cppyy::SizeOf(name+"::value_type"); + size_t typesz = Cppyy::SizeOfType(vtype); if (typesz) { PyObject* pyvalue_size = PyLong_FromSsize_t(typesz); PyObject_SetAttr(pyclass, PyStrings::gValueSize, pyvalue_size); @@ -1915,7 +1895,7 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name) Utility::AddToClass(pyclass, "__iter__", (PyCFunction)PyObject_SelfIter, METH_NOARGS); } - else if (name == "string" || name == "std::string") { // TODO: ask backend as well + else if (name == "std::basic_string") { // TODO: ask backend as well Utility::AddToClass(pyclass, "__repr__", (PyCFunction)STLStringRepr, METH_NOARGS); Utility::AddToClass(pyclass, "__str__", (PyCFunction)STLStringStr, METH_NOARGS); Utility::AddToClass(pyclass, "__bytes__", (PyCFunction)STLStringBytes, METH_NOARGS); @@ -1936,18 +1916,12 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name) ((PyTypeObject*)pyclass)->tp_hash = (hashfunc)STLStringHash; } - else if (name == "basic_string_view >" || name == "std::basic_string_view") { + else if (name == "std::basic_string_view") { Utility::AddToClass(pyclass, "__real_init", "__init__"); - Utility::AddToClass(pyclass, "__init__", (PyCFunction)StringViewInit, METH_VARARGS | METH_KEYWORDS); - Utility::AddToClass(pyclass, "__bytes__", (PyCFunction)STLViewStringBytes, METH_NOARGS); - Utility::AddToClass(pyclass, "__cmp__", (PyCFunction)STLViewStringCompare, METH_O); - Utility::AddToClass(pyclass, "__eq__", (PyCFunction)STLViewStringIsEqual, METH_O); - Utility::AddToClass(pyclass, "__ne__", (PyCFunction)STLViewStringIsNotEqual, METH_O); - Utility::AddToClass(pyclass, "__repr__", (PyCFunction)STLViewStringRepr, METH_NOARGS); - Utility::AddToClass(pyclass, "__str__", (PyCFunction)STLViewStringStr, METH_NOARGS); + Utility::AddToClass(pyclass, "__init__", (PyCFunction)StringViewInit, METH_VARARGS | METH_KEYWORDS); } - else if (name == "basic_string,allocator >" || name == "std::basic_string,std::allocator >") { + else if (name == "std::basic_string,std::allocator >") { Utility::AddToClass(pyclass, "__repr__", (PyCFunction)STLWStringRepr, METH_NOARGS); Utility::AddToClass(pyclass, "__str__", (PyCFunction)STLWStringStr, METH_NOARGS); Utility::AddToClass(pyclass, "__bytes__", (PyCFunction)STLWStringBytes, METH_NOARGS); diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Pythonize.h b/bindings/pyroot/cppyy/CPyCppyy/src/Pythonize.h index cbdec834a24cd..673f5740a73ff 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Pythonize.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Pythonize.h @@ -8,7 +8,7 @@ namespace CPyCppyy { // make the named C++ class more python-like -bool Pythonize(PyObject* pyclass, const std::string& name); +bool Pythonize(PyObject* pyclass, Cppyy::TCppScope_t scope); } // namespace CPyCppyy diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/SignalTryCatch.h b/bindings/pyroot/cppyy/CPyCppyy/src/SignalTryCatch.h index 453d8eb6ee388..14742b894099d 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/SignalTryCatch.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/SignalTryCatch.h @@ -40,6 +40,10 @@ using CppyyExceptionContext_t = CppyyLegacy::ExceptionContext_t; using CppyyExceptionContext_t = ExceptionContext_t; #endif +// FIXME: This is a dummy, replace with cling equivalent of gException +static CppyyExceptionContext_t DummyException; +static CppyyExceptionContext_t *gException = &DummyException; + #ifdef NEED_SIGJMP # define CLING_EXCEPTION_SETJMP(buf) sigsetjmp(buf,1) #else @@ -71,11 +75,6 @@ using CppyyExceptionContext_t = ExceptionContext_t; gException = R__old; \ } -// extern, defined in ROOT Core -#ifdef _MSC_VER -extern __declspec(dllimport) CppyyExceptionContext_t *gException; -#else -extern CppyyExceptionContext_t *gException; -#endif +CPYCPPYY_IMPORT CppyyExceptionContext_t *gException; #endif diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/TemplateProxy.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/TemplateProxy.cxx index b05d2ea61f039..795f635611dbe 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/TemplateProxy.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/TemplateProxy.cxx @@ -76,11 +76,9 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname, std::string proto = ""; #if PY_VERSION_HEX >= 0x03080000 -// adjust arguments for self if this is a rebound (global) function +// adjust arguments for self if this is a rebound global function bool isNS = (((CPPScope*)fTI->fPyClass)->fFlags & CPPScope::kIsNamespace); - if (!isNS && CPyCppyy_PyArgs_GET_SIZE(args, nargsf) && \ - (!fSelf || - (fSelf == Py_None && !Cppyy::IsStaticTemplate(((CPPScope*)fTI->fPyClass)->fCppType, fname)))) { + if (!isNS && !fSelf && CPyCppyy_PyArgs_GET_SIZE(args, nargsf)) { args += 1; nargsf -= 1; } @@ -142,12 +140,6 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname, } else PyErr_Clear(); - if (!bArgSet && (Py_TYPE(itemi) == &TemplateProxy_Type)) { - TemplateProxy *tp = (TemplateProxy*)itemi; - PyObject *tmpl_name = CPyCppyy_PyText_FromFormat("decltype(%s%s)", tp->fTI->fCppName.c_str(), tp->fTemplateArgs ? CPyCppyy_PyText_AsString(tp->fTemplateArgs) : ""); - PyTuple_SET_ITEM(tpArgs, i, tmpl_name); - bArgSet = true; - } if (!bArgSet) { // normal case (may well fail) PyErr_Clear(); @@ -424,7 +416,8 @@ static int tpp_doc_set(TemplateProxy* pytmpl, PyObject *val, void *) //= CPyCppyy template proxy callable behavior ================================ #define TPPCALL_RETURN \ -{ errors.clear(); \ +{ if (!errors.empty()) \ + std::for_each(errors.begin(), errors.end(), Utility::PyError_t::Clear);\ return result; } static inline std::string targs2str(TemplateProxy* pytmpl) @@ -631,7 +624,7 @@ static PyObject* tpp_call(TemplateProxy* pytmpl, PyObject* args, PyObject* kwds) PyObject* topmsg = CPyCppyy_PyText_FromFormat( "Could not find \"%s\" (set cppyy.set_debug() for C++ errors):", CPyCppyy_PyText_AsString(pyfullname)); Py_DECREF(pyfullname); - Utility::SetDetailedException(std::move(errors), topmsg /* steals */, PyExc_TypeError /* default error */); + Utility::SetDetailedException(errors, topmsg /* steals */, PyExc_TypeError /* default error */); return nullptr; } @@ -672,7 +665,7 @@ static PyObject* tpp_call(TemplateProxy* pytmpl, PyObject* args, PyObject* kwds) // error reporting is fraud, given the numerous steps taken, but more details seems better if (!errors.empty()) { PyObject* topmsg = CPyCppyy_PyText_FromString("Template method resolution failed:"); - Utility::SetDetailedException(std::move(errors), topmsg /* steals */, PyExc_TypeError /* default error */); + Utility::SetDetailedException(errors, topmsg /* steals */, PyExc_TypeError /* default error */); } else { PyErr_Format(PyExc_TypeError, "cannot resolve method template call for \'%s\'", pytmpl->fTI->fCppName.c_str()); @@ -735,21 +728,6 @@ static int tpp_setuseffi(CPPOverload*, PyObject*, void*) return 0; // dummy (__useffi__ unused) } -//----------------------------------------------------------------------------- -static PyObject* tpp_gettemplateargs(TemplateProxy* self, void*) { - if (!self->fTemplateArgs) { - Py_RETURN_NONE; - } - - Py_INCREF(self->fTemplateArgs); - return self->fTemplateArgs; -} - -//----------------------------------------------------------------------------- -static int tpp_settemplateargs(TemplateProxy*, PyObject*, void*) { - PyErr_SetString(PyExc_AttributeError, "__template_args__ is read-only"); - return -1; -} //---------------------------------------------------------------------------- static PyMappingMethods tpp_as_mapping = { @@ -760,9 +738,7 @@ static PyGetSetDef tpp_getset[] = { {(char*)"__doc__", (getter)tpp_doc, (setter)tpp_doc_set, nullptr, nullptr}, {(char*)"__useffi__", (getter)tpp_getuseffi, (setter)tpp_setuseffi, (char*)"unused", nullptr}, - {(char*)"__template_args__", (getter)tpp_gettemplateargs, (setter)tpp_settemplateargs, - (char*)"the template arguments for this method", nullptr}, - {(char*)nullptr, nullptr, nullptr, nullptr, nullptr}, + {(char*)nullptr, nullptr, nullptr, nullptr, nullptr} }; @@ -793,7 +769,6 @@ static PyObject* tpp_overload(TemplateProxy* pytmpl, PyObject* args) { // Select and call a specific C++ overload, based on its signature. const char* sigarg = nullptr; - const char* tmplarg = nullptr; PyObject* sigarg_tuple = nullptr; int want_const = -1; @@ -819,11 +794,6 @@ static PyObject* tpp_overload(TemplateProxy* pytmpl, PyObject* args) scope = ((CPPClass*)pytmpl->fTI->fPyClass)->fCppType; cppmeth = Cppyy::GetMethodTemplate( scope, pytmpl->fTI->fCppName, proto.substr(1, proto.size()-2)); - } else if (PyArg_ParseTuple(args, const_cast("ss:__overload__"), &sigarg, &tmplarg)) { - scope = ((CPPClass*)pytmpl->fTI->fPyClass)->fCppType; - std::string full_name = std::string(pytmpl->fTI->fCppName) + "<" + tmplarg + ">"; - - cppmeth = Cppyy::GetMethodTemplate(scope, full_name, sigarg); } else if (PyArg_ParseTuple(args, const_cast("O|i:__overload__"), &sigarg_tuple, &want_const)) { PyErr_Clear(); want_const = PyTuple_GET_SIZE(args) == 1 ? -1 : want_const; @@ -862,11 +832,17 @@ static PyObject* tpp_overload(TemplateProxy* pytmpl, PyObject* args) } // else attempt instantiation + PyObject* pytype = 0, *pyvalue = 0, *pytrace = 0; + PyErr_Fetch(&pytype, &pyvalue, &pytrace); + if (!cppmeth) { + PyErr_Restore(pytype, pyvalue, pytrace); return nullptr; } - PyErr_Clear(); + Py_XDECREF(pytype); + Py_XDECREF(pyvalue); + Py_XDECREF(pytrace); // TODO: the next step should be consolidated with Instantiate() PyCallable* meth = nullptr; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/TupleOfInstances.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/TupleOfInstances.cxx index 794684e245a94..6ef09a240d8d0 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/TupleOfInstances.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/TupleOfInstances.cxx @@ -128,7 +128,7 @@ PyTypeObject InstanceArrayIter_Type = { //= support for C-style arrays of objects ==================================== PyObject* TupleOfInstances_New( - Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, cdims_t dims) + Cppyy::TCppObject_t address, Cppyy::TCppScope_t klass, cdims_t dims) { // recursively set up tuples of instances on all dimensions if (dims.ndim() == UNKNOWN_SIZE || dims[0] == UNKNOWN_SIZE /* unknown shape or size */) { diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/TupleOfInstances.h b/bindings/pyroot/cppyy/CPyCppyy/src/TupleOfInstances.h index de95f29b2f8ba..d903872a3b7df 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/TupleOfInstances.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/TupleOfInstances.h @@ -30,7 +30,7 @@ inline bool TupleOfInstances_CheckExact(T* object) } PyObject* TupleOfInstances_New( - Cppyy::TCppObject_t address, Cppyy::TCppType_t klass, cdims_t dims); + Cppyy::TCppObject_t address, Cppyy::TCppScope_t klass, cdims_t dims); } // namespace CPyCppyy diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/TypeManip.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/TypeManip.cxx index 98bc183d25d55..bfead0bfc9575 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/TypeManip.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/TypeManip.cxx @@ -155,11 +155,14 @@ std::string CPyCppyy::TypeManip::template_base(const std::string& cppname) //---------------------------------------------------------------------------- std::string CPyCppyy::TypeManip::compound(const std::string& name) { +// FIXME: temporary fix for translation unit decl being passed in +// CreateExecutor from InitExecutor_ (run test10_object_identity), remove later + if (name.empty()) return ""; // Break down the compound of a fully qualified type name. std::string cleanName = remove_const(name); auto idx = find_qualifier_index(cleanName); - const std::string& cpd = cleanName.substr(idx, std::string::npos); + std::string cpd = cleanName.substr(idx, std::string::npos); // for easy identification of fixed size arrays if (!cpd.empty() && cpd.back() == ']') { @@ -168,9 +171,12 @@ std::string CPyCppyy::TypeManip::compound(const std::string& name) std::ostringstream scpd; scpd << cpd.substr(0, cpd.find('[')) << "[]"; - return scpd.str(); + cpd = scpd.str(); } + // XXX: remove this hack + if (!cpd.empty() && cpd[0] == ' ') return cpd.substr(1, cpd.length() - 1); + return cpd; } @@ -190,7 +196,7 @@ void CPyCppyy::TypeManip::cppscope_to_legalname(std::string& cppscope) { // Change characters illegal in a variable name into '_' to form a legal name. for (char& c : cppscope) { - for (char needle : {':', '>', '<', ' ', ',', '&', '=', '*'}) + for (char needle : {':', '>', '<', ' ', ',', '&', '=', '*', '-'}) if (c == needle) c = '_'; } } diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Utility.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/Utility.cxx index 41df97f1da24f..97e29ea7964b4 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Utility.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Utility.cxx @@ -201,7 +201,10 @@ bool CPyCppyy::Utility::AddToClass( PyObject* func = PyCFunction_New(pdef, nullptr); PyObject* name = CPyCppyy_PyText_InternFromString(pdef->ml_name); PyObject* method = CustomInstanceMethod_New(func, nullptr, pyclass); + PyObject* pytype = 0, *pyvalue = 0, *pytrace = 0; + PyErr_Fetch(&pytype, &pyvalue, &pytrace); bool isOk = PyType_Type.tp_setattro(pyclass, name, method) == 0; + PyErr_Restore(pytype, pyvalue, pytrace); Py_DECREF(method); Py_DECREF(name); Py_DECREF(func); @@ -266,14 +269,11 @@ CPyCppyy::PyCallable* BuildOperator(const std::string& lcname, const std::string const char* op, Cppyy::TCppScope_t scope, bool reverse=false) { // Helper to find a function with matching signature in 'funcs'. - std::string opname = "operator"; - opname += op; - Cppyy::TCppIndex_t idx = Cppyy::GetGlobalOperator(scope, lcname, rcname, opname); - if (idx == (Cppyy::TCppIndex_t)-1) + Cppyy::TCppMethod_t meth = Cppyy::GetGlobalOperator(scope, lcname, rcname, op); + if (!meth) return nullptr; - Cppyy::TCppMethod_t meth = Cppyy::GetMethod(scope, idx); if (!reverse) return new CPyCppyy::CPPFunction(scope, meth); return new CPyCppyy::CPPReverseBinary(scope, meth); @@ -332,15 +332,17 @@ CPyCppyy::PyCallable* CPyCppyy::Utility::FindBinaryOperator( if (!scope) { // TODO: the following should remain sync with what clingwrapper does in its // type remapper; there must be a better way? - if (lcname == "str" || lcname == "unicode" || lcname == "complex") + if (lcname == "str" || lcname == "unicode" || lcname == "complex" || lcname.find("std::") == 0) scope = Cppyy::GetScope("std"); - else scope = Cppyy::GetScope(TypeManip::extract_namespace(lcname)); } if (scope) pyfunc = BuildOperator(lcname, rcname, op, scope, reverse); + if (!pyfunc) + if ((scope = Cppyy::GetScope(TypeManip::extract_namespace(lcname)))) + pyfunc = BuildOperator(lcname, rcname, op, scope, reverse); - if (!pyfunc && scope != Cppyy::gGlobalScope) // search in global scope anyway - pyfunc = BuildOperator(lcname, rcname, op, Cppyy::gGlobalScope, reverse); + if (!pyfunc && scope != Cppyy::GetGlobalScope())// search in global scope anyway + pyfunc = BuildOperator(lcname, rcname, op, Cppyy::GetGlobalScope(), reverse); if (!pyfunc) { // For GNU on clang, search the internal __gnu_cxx namespace for binary operators (is @@ -355,7 +357,7 @@ CPyCppyy::PyCallable* CPyCppyy::Utility::FindBinaryOperator( if (!pyfunc) { // Same for clang (on Mac only?). TODO: find proper pre-processor magic to only use those // specific namespaces that are actually around; although to be sure, this isn't expensive. - static Cppyy::TCppScope_t std__1 = Cppyy::GetScope("std::__1"); + static Cppyy::TCppScope_t std__1 = Cppyy::GetFullScope("std::__1"); if (std__1 #ifdef __APPLE__ @@ -488,7 +490,8 @@ static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg, } if (CPPScope_Check(tn)) { - tmpl_name.append(full_scope(Cppyy::GetScopedFinalName(((CPPClass*)tn)->fCppType))); + auto cpp_type = Cppyy::GetScopedFinalName(((CPPClass*)tn)->fCppType); + tmpl_name.append(full_scope(cpp_type)); if (arg) { // try to specialize the type match for the given object CPPInstance* pyobj = (CPPInstance*)arg; @@ -552,9 +555,9 @@ static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg, PyErr_Clear(); // ctypes function pointer - PyObject* argtypes = nullptr; - PyObject* ret = nullptr; - if ((argtypes = PyObject_GetAttrString(arg, "argtypes")) && (ret = PyObject_GetAttrString(arg, "restype"))) { + PyObject* argtypes = PyObject_GetAttrString(arg, "argtypes"); + PyObject* ret = PyObject_GetAttrString(arg, "restype"); + if (argtypes && ret) { std::ostringstream tpn; PyObject* pytc = PyObject_GetAttr(ret, PyStrings::gCTypesType); tpn << CT2CppNameS(pytc, false) @@ -662,6 +665,250 @@ std::string CPyCppyy::Utility::ConstructTemplateArgs( } //---------------------------------------------------------------------------- +static bool AddTypeName(std::vector& types, PyObject* tn, + PyObject* arg, CPyCppyy::Utility::ArgPreference pref, int* pcnt = nullptr) +{ +// Determine the appropriate C++ type for a given Python type; this is a helper because +// it can recurse if the type is list or tuple and needs matching on std::vector. + using namespace CPyCppyy; + using namespace CPyCppyy::Utility; + + if (tn == (PyObject*)&PyInt_Type) { + if (arg) { +#if PY_VERSION_HEX < 0x03000000 + long l = PyInt_AS_LONG(arg); + types.push_back(Cppyy::GetType((l < INT_MIN || INT_MAX < l) ? "long" : "int")); +#else + PY_LONG_LONG ll = PyLong_AsLongLong(arg); + if (ll == (PY_LONG_LONG)-1 && PyErr_Occurred()) { + PyErr_Clear(); + PY_ULONG_LONG ull = PyLong_AsUnsignedLongLong(arg); + if (ull == (PY_ULONG_LONG)-1 && PyErr_Occurred()) { + PyErr_Clear(); + types.push_back(Cppyy::GetType("int")); // still out of range, will fail later + } else + types.push_back(Cppyy::GetType("unsigned long long")); // since already failed long long + } else + types.push_back(Cppyy::GetType((ll < INT_MIN || INT_MAX < ll) ? \ + ((ll < LONG_MIN || LONG_MAX < ll) ? "long long" : "long") : "int")); +#endif + } else { + types.push_back(Cppyy::GetType("int")); + } + + return true; + } + +#if PY_VERSION_HEX < 0x03000000 + if (tn == (PyObject*)&PyLong_Type) { + if (arg) { + PY_LONG_LONG ll = PyLong_AsLongLong(arg); + if (ll == (PY_LONG_LONG)-1 && PyErr_Occurred()) { + PyErr_Clear(); + PY_ULONG_LONG ull = PyLong_AsUnsignedLongLong(arg); + if (ull == (PY_ULONG_LONG)-1 && PyErr_Occurred()) { + PyErr_Clear(); + types.push_back(Cppyy::GetType("long")); // still out of range, will fail later + } else + types.push_back(Cppyy::GetType("unsigned long long")); // since already failed long long + } else + types.push_back(Cppyy::GetType((ll < LONG_MIN || LONG_MAX < ll) ? "long long" : "long")); + } else + types.push_back(Cppyy::GetType("long")); + + return true; + } +#endif + + if (tn == (PyObject*)&PyFloat_Type) { + // special case for floats (Python-speak for double) if from argument (only) + types.push_back(Cppyy::GetType(arg ? "double" : "float")); + return true; + } + +#if PY_VERSION_HEX < 0x03000000 + if (tn == (PyObject*)&PyString_Type) { +#else + if (tn == (PyObject*)&PyUnicode_Type) { +#endif + types.push_back(Cppyy::GetType("std::string", /* enable_slow_lookup */ true)); + return true; + } + + if (tn == (PyObject*)&PyList_Type || tn == (PyObject*)&PyTuple_Type) { + if (arg && PySequence_Size(arg)) { + std::string subtype{"std::initializer_list<"}; + PyObject* item = PySequence_GetItem(arg, 0); + ArgPreference subpref = pref == kValue ? kValue : kPointer; + if (AddTypeName(subtype, (PyObject*)Py_TYPE(item), item, subpref)) { + subtype.append(">"); + types.push_back(Cppyy::GetType(subtype)); + } + Py_DECREF(item); + } + + return true; + } + + if (CPPScope_Check(tn)) { + auto cpp_type = Cppyy::GetTypeFromScope(((CPPClass*)tn)->fCppType); + if (arg) { + // try to specialize the type match for the given object + CPPInstance* pyobj = (CPPInstance*)arg; + if (CPPInstance_Check(pyobj)) { + if (pyobj->fFlags & CPPInstance::kIsRValue) + cpp_type = + Cppyy::GetReferencedType(cpp_type, /*rvalue=*/true); + else { + if (pcnt) *pcnt += 1; + if ((pyobj->fFlags & CPPInstance::kIsReference) || pref == kPointer) + cpp_type = Cppyy::GetPointerType(cpp_type); + else if (pref != kValue) + cpp_type = + Cppyy::GetReferencedType(cpp_type, /*rvalue=*/false); + } + } + } + types.push_back(cpp_type); + return true; + } + + if (tn == (PyObject*)&CPPOverload_Type) { + PyObject* tpName = arg ? \ + PyObject_GetAttr(arg, PyStrings::gCppName) : \ + CPyCppyy_PyText_FromString("void* (*)(...)"); + types.push_back(Cppyy::GetType(CPyCppyy_PyText_AsString(tpName), /* enable_slow_lookup */ true)); + Py_DECREF(tpName); + + return true; + } + + if (arg && PyCallable_Check(arg)) { + PyObject* annot = PyObject_GetAttr(arg, PyStrings::gAnnotations); + if (annot) { + if (PyDict_Check(annot) && 1 < PyDict_Size(annot)) { + PyObject* ret = PyDict_GetItemString(annot, "return"); + if (ret) { + // dict is ordered, with the last value being the return type + std::ostringstream tpn; + tpn << (CPPScope_Check(ret) ? ClassName(ret) : CPyCppyy_PyText_AsString(ret)) + << " (*)("; + + PyObject* values = PyDict_Values(annot); + for (Py_ssize_t i = 0; i < (PyList_GET_SIZE(values)-1); ++i) { + if (i) tpn << ", "; + PyObject* item = PyList_GET_ITEM(values, i); + tpn << (CPPScope_Check(item) ? ClassName(item) : CPyCppyy_PyText_AsString(item)); + } + Py_DECREF(values); + + tpn << ')'; + // tmpl_name.append(tpn.str()); + // FIXME: find a way to add it to types + throw std::runtime_error( + "This path is not yet implemented (AddTypeName) \n"); + + return true; + + } else + PyErr_Clear(); + } + Py_DECREF(annot); + } else + PyErr_Clear(); + + PyObject* tpName = PyObject_GetAttr(arg, PyStrings::gCppName); + if (tpName) { + types.push_back(Cppyy::GetType(CPyCppyy_PyText_AsString(tpName), /* enable_slow_lookup */ true)); + Py_DECREF(tpName); + return true; + } + PyErr_Clear(); + } + + for (auto nn : {PyStrings::gCppName, PyStrings::gName}) { + PyObject* tpName = PyObject_GetAttr(tn, nn); + if (tpName) { + Cppyy::TCppType_t type = Cppyy::GetType(CPyCppyy_PyText_AsString(tpName), /* enable_slow_lookup */ true); + if (Cppyy::IsEnumType(type)) { + PyObject *value_int = PyNumber_Index(tn); + if (!value_int) { + types.push_back(type); + PyErr_Clear(); + } else { + PyObject* pystr = PyObject_Str(tn); + std::string num = CPyCppyy_PyText_AsString(pystr); + types.push_back({type, strdup(num.c_str())}); + Py_DECREF(pystr); + Py_DECREF(value_int); + } + } else { + types.push_back(type); + } + Py_DECREF(tpName); + return true; + } + PyErr_Clear(); + } + + if (PyInt_Check(tn) || PyLong_Check(tn) || PyFloat_Check(tn)) { + // last ditch attempt, works for things like int values; since this is a + // source of errors otherwise, it is limited to specific types and not + // generally used (str(obj) can print anything ...) + PyObject* pystr = PyObject_Str(tn); + std::string num = CPyCppyy_PyText_AsString(pystr); + if (num == "True") + num = "1"; + else if (num == "False") + num = "0"; + types.push_back({Cppyy::GetType("int"), strdup(num.c_str())}); + Py_DECREF(pystr); + return true; + } + + return false; +} + +std::vector CPyCppyy::Utility::GetTemplateArgsTypes( + PyObject* /*scope*/, PyObject* tpArgs, PyObject* args, ArgPreference pref, int argoff, int* pcnt) +{ +// Helper to construct the "" part of a templated name (either +// for a class or method lookup + bool justOne = !PyTuple_CheckExact(tpArgs); + +// Note: directly appending to string is a lot faster than stringstream + std::vector types; + types.reserve(8); + + if (pcnt) *pcnt = 0; // count number of times 'pref' is used + + Py_ssize_t nArgs = justOne ? 1 : PyTuple_GET_SIZE(tpArgs); + for (int i = argoff; i < nArgs; ++i) { + // add type as string to name + PyObject* tn = justOne ? tpArgs : PyTuple_GET_ITEM(tpArgs, i); + if (CPyCppyy_PyText_Check(tn)) { + const char * tn_string = CPyCppyy_PyText_AsString(tn); + + if (Cppyy::AppendTypesSlow(tn_string, types)) { + PyErr_Format(PyExc_TypeError, + "Cannot find Templated Arg: %s", tn_string); + return {}; + } + + // some commmon numeric types (separated out for performance: checking for + // __cpp_name__ and/or __name__ is rather expensive) + } else { + if (!AddTypeName(types, tn, (args ? PyTuple_GET_ITEM(args, i) : nullptr), pref, pcnt)) { + PyErr_SetString(PyExc_TypeError, + "could not construct C++ name from provided template argument."); + return {}; + } + } + } + + return types; +} + std::string CPyCppyy::Utility::CT2CppNameS(PyObject* pytc, bool allow_voidp) { // helper to convert ctypes' `_type_` info to the equivalent C++ name @@ -705,7 +952,7 @@ void CPyCppyy::Utility::ConstructCallbackPreamble(const std::string& retType, int nArgs = (int)argtypes.size(); // return value and argument type converters - bool isVoid = retType == "void"; + bool isVoid = retType.find("void") == 0; // might contain trailing space if (!isVoid) code << " CPYCPPYY_STATIC std::unique_ptr> " "retconv{CPyCppyy::CreateConverter(\"" @@ -767,7 +1014,7 @@ void CPyCppyy::Utility::ConstructCallbackPreamble(const std::string& retType, void CPyCppyy::Utility::ConstructCallbackReturn(const std::string& retType, int nArgs, std::ostringstream& code) { // Generate code for return value conversion and error handling. - bool isVoid = retType == "void"; + bool isVoid = retType.find("void") == 0; // might contain trailing space bool isPtr = Cppyy::ResolveName(retType).back() == '*'; if (nArgs) @@ -895,7 +1142,7 @@ Py_ssize_t CPyCppyy::Utility::GetBuffer(PyObject* pyobject, char tc, int size, v if (PyObject_CheckBuffer(pyobject)) { if (PySequence_Check(pyobject) && !PySequence_Size(pyobject)) return 0; // PyObject_GetBuffer() crashes on some platforms for some zero-sized seqeunces - PyErr_Clear(); + Py_buffer bufinfo; memset(&bufinfo, 0, sizeof(Py_buffer)); if (PyObject_GetBuffer(pyobject, &bufinfo, PyBUF_FORMAT) == 0) { @@ -981,14 +1228,15 @@ Py_ssize_t CPyCppyy::Utility::GetBuffer(PyObject* pyobject, char tc, int size, v buf = 0; // not compatible // clarify error message - auto error = FetchPyError(); + PyObject* pytype = 0, *pyvalue = 0, *pytrace = 0; + PyErr_Fetch(&pytype, &pyvalue, &pytrace); PyObject* pyvalue2 = CPyCppyy_PyText_FromFormat( (char*)"%s and given element size (%ld) do not match needed (%d)", - CPyCppyy_PyText_AsString(error.fValue.get()), + CPyCppyy_PyText_AsString(pyvalue), seqmeths->sq_length ? (long)(buflen/(*(seqmeths->sq_length))(pyobject)) : (long)buflen, size); - error.fValue.reset(pyvalue2); - RestorePyError(error); + Py_DECREF(pyvalue); + PyErr_Restore(pytype, pyvalue2, pytrace); } } @@ -1016,13 +1264,7 @@ std::string CPyCppyy::Utility::MapOperatorName(const std::string& name, bool bTa if (gOpRemove.find(op) != gOpRemove.end()) return ""; - // check first if none, to prevent spurious deserializing downstream TC2POperatorMapping_t::iterator pop = gC2POperatorMapping.find(op); - if (pop == gC2POperatorMapping.end() && gOpSkip.find(op) == gOpSkip.end()) { - op = Cppyy::ResolveName(op); - pop = gC2POperatorMapping.find(op); - } - // map C++ operator to python equivalent, or made up name if no equivalent exists if (pop != gC2POperatorMapping.end()) { return pop->second; @@ -1145,50 +1387,20 @@ PyObject* CPyCppyy::Utility::PyErr_Occurred_WithGIL() } -//---------------------------------------------------------------------------- -CPyCppyy::Utility::PyError_t CPyCppyy::Utility::FetchPyError() -{ - // create a PyError_t RAII object that will capture and store the exception data - CPyCppyy::Utility::PyError_t error{}; -#if PY_VERSION_HEX >= 0x030c0000 - error.fValue.reset(PyErr_GetRaisedException()); -#else - PyObject *pytype = nullptr; - PyObject *pyvalue = nullptr; - PyObject *pytrace = nullptr; - PyErr_Fetch(&pytype, &pyvalue, &pytrace); - error.fType.reset(pytype); - error.fValue.reset(pyvalue); - error.fTrace.reset(pytrace); -#endif - return error; -} - - -//---------------------------------------------------------------------------- -void CPyCppyy::Utility::RestorePyError(CPyCppyy::Utility::PyError_t &error) -{ -#if PY_VERSION_HEX >= 0x030c0000 - PyErr_SetRaisedException(error.fValue.release()); -#else - PyErr_Restore(error.fType.release(), error.fValue.release(), error.fTrace.release()); -#endif -} - - //---------------------------------------------------------------------------- size_t CPyCppyy::Utility::FetchError(std::vector& errors, bool is_cpp) { // Fetch the current python error, if any, and store it for future use. if (PyErr_Occurred()) { - errors.emplace_back(FetchPyError()); - errors.back().fIsCpp = is_cpp; + PyError_t e{is_cpp}; + PyErr_Fetch(&e.fType, &e.fValue, &e.fTrace); + errors.push_back(e); } return errors.size(); } //---------------------------------------------------------------------------- -void CPyCppyy::Utility::SetDetailedException(std::vector&& errors, PyObject* topmsg, PyObject* defexc) +void CPyCppyy::Utility::SetDetailedException(std::vector& errors, PyObject* topmsg, PyObject* defexc) { // Use the collected exceptions to build up a detailed error log. if (errors.empty()) { @@ -1219,18 +1431,14 @@ void CPyCppyy::Utility::SetDetailedException(std::vector&& errors, Py // bind the original C++ object, rather than constructing from topmsg, as it // is expected to have informative state - RestorePyError(*unique_from_cpp); + Py_INCREF(unique_from_cpp->fType); Py_INCREF(unique_from_cpp->fValue); Py_XINCREF(unique_from_cpp->fTrace); + PyErr_Restore(unique_from_cpp->fType, unique_from_cpp->fValue, unique_from_cpp->fTrace); } else { // try to consolidate Python exceptions, otherwise select default PyObject* exc_type = nullptr; for (auto& e : errors) { -#if PY_VERSION_HEX >= 0x030c0000 - PyObject* pytype = (PyObject*)Py_TYPE(e.fValue.get()); -#else - PyObject* pytype = e.fType.get(); -#endif - if (!exc_type) exc_type = pytype; - else if (exc_type != pytype) { + if (!exc_type) exc_type = e.fType; + else if (exc_type != e.fType) { exc_type = defexc; break; } @@ -1239,15 +1447,14 @@ void CPyCppyy::Utility::SetDetailedException(std::vector&& errors, Py // add the details to the topmsg PyObject* separator = CPyCppyy_PyText_FromString("\n "); for (auto& e : errors) { - PyObject *pyvalue = e.fValue.get(); CPyCppyy_PyText_Append(&topmsg, separator); - if (CPyCppyy_PyText_Check(pyvalue)) { - CPyCppyy_PyText_Append(&topmsg, pyvalue); - } else if (pyvalue) { - PyObject* excstr = PyObject_Str(pyvalue); + if (CPyCppyy_PyText_Check(e.fValue)) { + CPyCppyy_PyText_Append(&topmsg, e.fValue); + } else if (e.fValue) { + PyObject* excstr = PyObject_Str(e.fValue); if (!excstr) { PyErr_Clear(); - excstr = PyObject_Str((PyObject*)Py_TYPE(pyvalue)); + excstr = PyObject_Str((PyObject*)Py_TYPE(e.fValue)); } CPyCppyy_PyText_AppendAndDel(&topmsg, excstr); } else { @@ -1262,6 +1469,8 @@ void CPyCppyy::Utility::SetDetailedException(std::vector&& errors, Py PyErr_SetString(exc_type, CPyCppyy_PyText_AsString(topmsg)); } +// cleanup stored errors and done with topmsg (whether used or not) + std::for_each(errors.begin(), errors.end(), PyError_t::Clear); Py_DECREF(topmsg); } diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Utility.h b/bindings/pyroot/cppyy/CPyCppyy/src/Utility.h index 68bee557aaa24..0290661deb70e 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Utility.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Utility.h @@ -3,7 +3,6 @@ // Standard #include -#include #include #include @@ -40,6 +39,9 @@ PyCallable* FindBinaryOperator(const std::string& lcname, const std::string& rcn enum ArgPreference { kNone, kPointer, kReference, kValue }; std::string ConstructTemplateArgs( PyObject* pyname, PyObject* tpArgs, PyObject* args = nullptr, ArgPreference = kNone, int argoff = 0, int* pcnt = nullptr); +std::vector GetTemplateArgsTypes( + PyObject* scope, PyObject* tpArgs, PyObject* args = nullptr, ArgPreference = kNone, int argoff = 0, int* pcnt = nullptr); + std::string CT2CppNameS(PyObject* pytc, bool allow_voidp); inline PyObject* CT2CppName(PyObject* pytc, const char* cpd, bool allow_voidp) { @@ -96,23 +98,22 @@ PyObject* PyErr_Occurred_WithGIL(); // helpers for collecting/maintaining python exception data struct PyError_t { - struct PyObjectDeleter { - void operator()(PyObject *obj) { Py_XDECREF(obj); } - }; -#if PY_VERSION_HEX < 0x030c0000 - std::unique_ptr fType; - std::unique_ptr fTrace; -#endif - std::unique_ptr fValue; - bool fIsCpp = false; -}; + PyError_t(bool is_cpp = false) : fIsCpp(is_cpp) { fType = fValue = fTrace = 0; } -PyError_t FetchPyError(); -void RestorePyError(PyError_t &error); + static void Clear(PyError_t& e) + { + // Remove exception information. + Py_XDECREF(e.fType); Py_XDECREF(e.fValue); Py_XDECREF(e.fTrace); + e.fType = e.fValue = e.fTrace = 0; + } + + PyObject *fType, *fValue, *fTrace; + bool fIsCpp; +}; size_t FetchError(std::vector&, bool is_cpp = false); void SetDetailedException( - std::vector&& errors /* clears */, PyObject* topmsg /* steals ref */, PyObject* defexc); + std::vector& errors /* clears */, PyObject* topmsg /* steals ref */, PyObject* defexc); // setup Python API for callbacks bool IncludePython(); diff --git a/bindings/pyroot/cppyy/cppyy-backend/CMakeLists.txt b/bindings/pyroot/cppyy/cppyy-backend/CMakeLists.txt index 0aceeef318229..8f46b174d00dd 100644 --- a/bindings/pyroot/cppyy/cppyy-backend/CMakeLists.txt +++ b/bindings/pyroot/cppyy/cppyy-backend/CMakeLists.txt @@ -4,6 +4,12 @@ # For the licensing terms see $ROOTSYS/LICENSE. # For the list of contributors see $ROOTSYS/README/CREDITS. -add_library(cppyy_backend STATIC clingwrapper/src/clingwrapper.cxx) +add_library(cppyy_backend STATIC + clingwrapper/src/clingwrapper.cxx + clingwrapper/src/cppinterop_dispatch.cxx) + +target_include_directories(cppyy_backend PRIVATE + ${CMAKE_SOURCE_DIR}/interpreter/CppInterOp/include +) target_link_libraries(cppyy_backend Core) target_compile_options(cppyy_backend PRIVATE -fPIC) diff --git a/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/callcontext.h b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/callcontext.h index edd7ca522c864..5f729410e4c2c 100644 --- a/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/callcontext.h +++ b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/callcontext.h @@ -8,7 +8,7 @@ #include "cpp_cppyy.h" //ROOT -#include "Rtypes.h" +// #include "Rtypes.h" namespace CPyCppyy { diff --git a/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/clingwrapper.cxx b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/clingwrapper.cxx index 6343d2bc16b72..30ec2776b621d 100644 --- a/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/clingwrapper.cxx +++ b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/clingwrapper.cxx @@ -1,4 +1,4 @@ -#ifndef WIN32 +#ifndef _WIN32 #ifndef _CRT_SECURE_NO_WARNINGS // silence warnings about getenv, strncpy, etc. #define _CRT_SECURE_NO_WARNINGS @@ -38,18 +38,24 @@ #include "TSystem.h" #include "TThread.h" +#include +// #endif + +// +// Symbol exposing facility + // Standard -#include +#include #include // for std::count, std::remove -#include #include #include #include +#include #include #include -#include -#include // for getenv -#include +#include +#include // for getenv +#include #include #if defined(__arm64__) @@ -112,115 +118,59 @@ class ARMUncaughtException { #endif // small number that allows use of stack for argument passing -const int SMALL_ARGS_N = 8; +// const int SMALL_ARGS_N = 8; // convention to pass flag for direct calls (similar to Python's vector calls) -#define DIRECT_CALL ((size_t)1 << (8 * sizeof(size_t) - 1)) -static inline size_t CALL_NARGS(size_t nargs) { - return nargs & ~DIRECT_CALL; -} +// #define DIRECT_CALL ((size_t)1 << (8 * sizeof(size_t) - 1)) +// static inline size_t CALL_NARGS(size_t nargs) { +// return nargs & ~DIRECT_CALL; +// } // data for life time management --------------------------------------------- -typedef std::vector ClassRefs_t; -static ClassRefs_t g_classrefs(1); -static const ClassRefs_t::size_type GLOBAL_HANDLE = 1; -static const ClassRefs_t::size_type STD_HANDLE = GLOBAL_HANDLE + 1; - -typedef std::map Name2ClassRefIndex_t; -static Name2ClassRefIndex_t g_name2classrefidx; - -static std::map resolved_enum_types; - -namespace { - -static inline -Cppyy::TCppType_t find_memoized_scope(const std::string& name) -{ - auto icr = g_name2classrefidx.find(name); - if (icr != g_name2classrefidx.end()) - return (Cppyy::TCppType_t)icr->second; - return (Cppyy::TCppType_t)0; -} - -static inline -std::string find_memoized_resolved_name(const std::string& name) -{ -// resolved class types - Cppyy::TCppType_t klass = find_memoized_scope(name); - if (klass) return Cppyy::GetScopedFinalName(klass); - -// resolved enum types - auto res = resolved_enum_types.find(name); - if (res != resolved_enum_types.end()) - return res->second; - -// unknown ... - return ""; -} - -class CallWrapper { -public: - typedef const void* DeclId_t; - -public: - CallWrapper(TFunction* f) : fDecl(f->GetDeclId()), fName(f->GetName()), fTF(new TFunction(*f)) {} - CallWrapper(DeclId_t fid, const std::string& n) : fDecl(fid), fName(n), fTF(nullptr) {} - ~CallWrapper() { - delete fTF; - } - -public: - TInterpreter::CallFuncIFacePtr_t fFaceptr; - DeclId_t fDecl; - std::string fName; - TFunction* fTF; -}; - -} - -static std::vector gWrapperHolder; - -static inline -CallWrapper* new_CallWrapper(TFunction* f) -{ - CallWrapper* wrap = new CallWrapper(f); - gWrapperHolder.push_back(wrap); - return wrap; -} - -static inline -CallWrapper* new_CallWrapper(CallWrapper::DeclId_t fid, const std::string& n) -{ - CallWrapper* wrap = new CallWrapper(fid, n); - gWrapperHolder.push_back(wrap); - return wrap; -} - -typedef std::vector GlobalVars_t; -typedef std::map GlobalVarsIndices_t; - -static GlobalVars_t g_globalvars; -static GlobalVarsIndices_t g_globalidx; - -static std::set gSTLNames; - - -// data ---------------------------------------------------------------------- -Cppyy::TCppScope_t Cppyy::gGlobalScope = GLOBAL_HANDLE; - -// builtin types (including a few common STL templates as long as they live in -// the global namespace b/c of choices upstream) +// typedef std::vector ClassRefs_t; +// static ClassRefs_t g_classrefs(1); +// static const ClassRefs_t::size_type GLOBAL_HANDLE = 1; +// static const ClassRefs_t::size_type STD_HANDLE = GLOBAL_HANDLE + 1; + +// typedef std::map Name2ClassRefIndex_t; +// static Name2ClassRefIndex_t g_name2classrefidx; + +// namespace { + +// static inline +// Cppyy::TCppType_t find_memoized(const std::string& name) +// { +// auto icr = g_name2classrefidx.find(name); +// if (icr != g_name2classrefidx.end()) +// return (Cppyy::TCppType_t)icr->second; +// return (Cppyy::TCppType_t)0; +// } +// +// } // namespace +// +// static inline +// CallWrapper* new_CallWrapper(CppyyLegacy::TFunction* f) +// { +// CallWrapper* wrap = new CallWrapper(f); +// gWrapperHolder.push_back(wrap); +// return wrap; +// } +// + +// typedef std::vector GlobalVars_t; +// typedef std::map GlobalVarsIndices_t; + +// static GlobalVars_t g_globalvars; +// static GlobalVarsIndices_t g_globalidx; + +// using CppDispatch::Dispatch; +// builtin types static std::set g_builtins = {"bool", "char", "signed char", "unsigned char", "wchar_t", "short", "unsigned short", "int", "unsigned int", "long", "unsigned long", "long long", "unsigned long long", - "float", "double", "long double", "void", - "allocator", "array", "basic_string", "complex", "initializer_list", "less", "list", + "float", "double", "long double", "void", "allocator", "array", "basic_string", "complex", "initializer_list", "less", "list", "map", "pair", "set", "vector"}; -// smart pointer types -static std::set gSmartPtrTypes = - {"auto_ptr", "std::auto_ptr", "shared_ptr", "std::shared_ptr", - "unique_ptr", "std::unique_ptr", "weak_ptr", "std::weak_ptr"}; // to filter out ROOT names static std::set gInitialNames; @@ -233,6 +183,8 @@ static bool gEnableFastPath = true; // global initialization ----------------------------------------------------- namespace { +const int kMAXSIGNALS = 16; + // names copied from TUnixSystem #ifdef WIN32 const int SIGBUS = 0; // simple placeholders for ones that don't exist @@ -269,54 +221,126 @@ static struct Signalmap_t { { SIGUSR2, "user-defined signal 2" } }; -static void inline do_trace(int sig) { - std::cerr << " *** Break *** " << (sig < kMAXSIGNALS ? gSignalMap[sig].fSigName : "") << std::endl; - gSystem->StackTrace(); -} - -class TExceptionHandlerImp : public TExceptionHandler { -public: - void HandleException(Int_t sig) override { - if (TROOT::Initialized()) { - if (gException) { - gInterpreter->RewindDictionary(); - gInterpreter->ClearFileBusy(); - } - - if (!std::getenv("CPPYY_CRASH_QUIET")) - do_trace(sig); +// static void inline do_trace(int sig) { +// std::cerr << " *** Break *** " << (sig < kMAXSIGNALS ? gSignalMap[sig].fSigName : "") << std::endl; +// gSystem->StackTrace(); +// } + +// class TExceptionHandlerImp : public TExceptionHandler { +// public: +// virtual void HandleException(Int_t sig) { +// if (TROOT::Initialized()) { +// if (gException) { +// gInterpreter->RewindDictionary(); +// gInterpreter->ClearFileBusy(); +// } +// +// if (!getenv("CPPYY_CRASH_QUIET")) +// do_trace(sig); +// +// // jump back, if catch point set +// Throw(sig); +// } +// +// do_trace(sig); +// gSystem->Exit(128 + sig); +// } +// }; - // jump back, if catch point set - Throw(sig); - } +static inline +void push_tokens_from_string(char *s, std::vector &tokens) { + char *token = strtok(s, " "); - do_trace(sig); - gSystem->Exit(128 + sig); + while (token) { + tokens.push_back(token); + token = strtok(NULL, " "); } -}; +} + +static inline +bool is_integral(std::string& s) +{ + if (s == "false") { s = "0"; return true; } + else if (s == "true") { s = "1"; return true; } + return !s.empty() && std::find_if(s.begin(), + s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end(); +} class ApplicationStarter { + CppDispatch::TInterp_t Interp; public: ApplicationStarter() { - // initialize ROOT early to guarantee proper order of shutdown later on (gROOT is a - // macro that resolves to the ::CppyyLegacy::GetROOT() function call) + // Check if somebody already loaded CppInterOp and created an + // interpreter for us. (void)gROOT; - // setup dummy holders for global and std namespaces - assert(g_classrefs.size() == GLOBAL_HANDLE); - g_name2classrefidx[""] = GLOBAL_HANDLE; - g_classrefs.push_back(TClassRef("")); - // aliases for std (setup already in pythonify) - g_name2classrefidx["std"] = STD_HANDLE; - g_name2classrefidx["::std"] = g_name2classrefidx["std"]; - g_classrefs.push_back(TClassRef("std")); + // + + char *libcling = gSystem->DynamicPathName("libCling"); + void *gInterpreterLib = dlopen(libcling, RTLD_LAZY|RTLD_LOCAL); + + std::cout<<"INITIALIZING CPPYY BACKEND\n"; + std::cout<<"HANDLE\n"<(dlsym(gInterpreterLib, "CppGetProcAddress")); + + + // if( CppDispatch::GetInterpreter()) { + // std::cout<<"INTERPRETER ALREADY EXISTS\n"; + // std::string code = R"( + // class TestNS { + // public: + // int add(int x, int y, int z) { return x + y; } + // int add(double x, double y) { return x + y; } + // }; + // )"; + // CppDispatch::Declare(code.c_str(), false); + // CppDispatch::TCppScope_t globalscope = CppDispatch::GetScope("TestNS", 0); + // std::cout<<"gs: "< methods; + // CppDispatch::GetClassMethods(globalscope, methods); + // for (auto m : methods) { + // std::cout<<"METHODS NAME: "< InterpArgs({"-std=c++17"}); +#else + std::vector InterpArgs( + {"-std=c++17", "-march=native"}); +#endif +#else + std::vector InterpArgs({"-std=c++17", "-march=native"}); +#endif + char *InterpArgString = getenv("CPPINTEROP_EXTRA_INTERPRETER_ARGS"); + + if (InterpArgString) + push_tokens_from_string(InterpArgString, InterpArgs); - // add a dummy global to refer to as null at index 0 - g_globalvars.push_back(nullptr); - g_globalidx[nullptr] = 0; +#ifdef __arm64__ +#ifdef __APPLE__ + // If on apple silicon don't use -march=native + Interp = CppDispatch::CreateInterpreter({"-std=c++17"}); +#else + Interp = CppDispatch::CreateInterpreter({"-std=c++17", "-march=native"}); +#endif +#else + Interp = CppDispatch::CreateInterpreter({"-std=c++17", "-march=native"}, {}); +#endif + } - // fill out the builtins + // fill out the builtins std::set bi{g_builtins}; for (const auto& name : bi) { for (const char* a : {"*", "&", "*&", "[]", "*[]"}) @@ -324,638 +348,543 @@ class ApplicationStarter { } // disable fast path if requested - if (std::getenv("CPPYY_DISABLE_FASTPATH")) gEnableFastPath = false; - - // fill the set of STL names - const char* stl_names[] = {"allocator", "auto_ptr", "bad_alloc", "bad_cast", - "bad_exception", "bad_typeid", "basic_filebuf", "basic_fstream", "basic_ifstream", - "basic_ios", "basic_iostream", "basic_istream", "basic_istringstream", - "basic_ofstream", "basic_ostream", "basic_ostringstream", "basic_streambuf", - "basic_string", "basic_stringbuf", "basic_stringstream", "binary_function", - "binary_negate", "bitset", "byte", "char_traits", "codecvt_byname", "codecvt", "collate", - "collate_byname", "compare", "complex", "ctype_byname", "ctype", "default_delete", - "deque", "divides", "domain_error", "equal_to", "exception", "forward_list", "fpos", - "function", "greater_equal", "greater", "gslice_array", "gslice", "hash", "indirect_array", - "integer_sequence", "invalid_argument", "ios_base", "istream_iterator", "istreambuf_iterator", - "istrstream", "iterator_traits", "iterator", "length_error", "less_equal", "less", - "list", "locale", "localedef utility", "locale utility", "logic_error", "logical_and", - "logical_not", "logical_or", "map", "mask_array", "mem_fun", "mem_fun_ref", "messages", - "messages_byname", "minus", "modulus", "money_get", "money_put", "moneypunct", - "moneypunct_byname", "multimap", "multiplies", "multiset", "negate", "not_equal_to", - "num_get", "num_put", "numeric_limits", "numpunct", "numpunct_byname", - "ostream_iterator", "ostreambuf_iterator", "ostrstream", "out_of_range", - "overflow_error", "pair", "plus", "pointer_to_binary_function", - "pointer_to_unary_function", "priority_queue", "queue", "range_error", - "raw_storage_iterator", "reverse_iterator", "runtime_error", "set", "shared_ptr", - "slice_array", "slice", "stack", "string", "strstream", "strstreambuf", - "time_get_byname", "time_get", "time_put_byname", "time_put", "unary_function", - "unary_negate", "unique_ptr", "underflow_error", "unordered_map", "unordered_multimap", - "unordered_multiset", "unordered_set", "valarray", "vector", "weak_ptr", "wstring", - "__hash_not_enabled"}; - for (auto& name : stl_names) - gSTLNames.insert(name); + if (getenv("CPPYY_DISABLE_FASTPATH")) gEnableFastPath = false; // set opt level (default to 2 if not given; Cling itself defaults to 0) int optLevel = 2; - if (std::getenv("CPPYY_OPT_LEVEL")) optLevel = atoi(std::getenv("CPPYY_OPT_LEVEL")); + + if (getenv("CPPYY_OPT_LEVEL")) optLevel = atoi(getenv("CPPYY_OPT_LEVEL")); + if (optLevel != 0) { std::ostringstream s; s << "#pragma cling optimize " << optLevel; - gInterpreter->ProcessLine(s.str().c_str()); + CppDispatch::Process(s.str().c_str()); } - // load frequently used headers + // This would give us something like: + // /home/vvassilev/workspace/builds/scratch/cling-build/builddir/lib/clang/13.0.0 + const char * ResourceDir = CppDispatch::GetResourceDir(); + std::string ClingSrc = std::string(ResourceDir) + "/../../../../cling-src"; + std::string ClingBuildDir = std::string(ResourceDir) + "/../../../"; + CppDispatch::AddIncludePath((ClingSrc + "/tools/cling/include").c_str()); + CppDispatch::AddIncludePath((ClingSrc + "/include").c_str()); + CppDispatch::AddIncludePath((ClingBuildDir + "/include").c_str()); + CppDispatch::AddIncludePath("/home/ajomy/cppyy-interop-dev/CppInterOp/cppyy-backend/clingwrapper/src"); + CppDispatch::AddIncludePath("/home/ajomy/ROOT/root_src/interpreter/CppInterOp/include"); + CppDispatch::LoadLibrary("libstdc++", /* lookup= */ true); + + // load frequently used headers const char* code = - "#include \n" - "#include \n" - "#include \n" // defines R__EXTERN - "#include \n" - "#include "; - gInterpreter->ProcessLine(code); + "#include \n" + "#include \n" + "#include \n" + "#include \n" + "#include \n" // for strcpy + "#include \n" + // "#include \n" // defines R__EXTERN + "#include \n" + "#include \n" + "#include \n" + "#include \n" // for the dispatcher code to use + // std::function + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#include \n" // FIXME: Replace with modules + "#if __has_include()\n" + "#include \n" + "#endif\n" + "#include \n"; + CppDispatch::Process(code); // create helpers for comparing thingies - gInterpreter->Declare( + CppDispatch::Declare( "namespace __cppyy_internal { template" - " bool is_equal(const C1& c1, const C2& c2) { return (bool)(c1 == c2); } }"); - gInterpreter->Declare( + " bool is_equal(const C1& c1, const C2& c2) { return (bool)(c1 == c2); } }", false); + CppDispatch::Declare( "namespace __cppyy_internal { template" - " bool is_not_equal(const C1& c1, const C2& c2) { return (bool)(c1 != c2); } }"); + " bool is_not_equal(const C1& c1, const C2& c2) { return (bool)(c1 != c2); } }", false); + + // Define gCling when we run with clang-repl. + // FIXME: We should get rid of all the uses of gCling as this seems to + // break encapsulation. + std::stringstream InterpPtrSS; + InterpPtrSS << "#ifndef __CLING__\n" + << "namespace cling { namespace runtime {\n" + << "void* gCling=(void*)" << static_cast(Interp) + << ";\n }}\n" + << "#endif \n"; + CppDispatch::Process(InterpPtrSS.str().c_str()); // helper for multiple inheritance - gInterpreter->Declare("namespace __cppyy_internal { struct Sep; }"); - - // retrieve all initial (ROOT) C++ names in the global scope to allow filtering later - gROOT->GetListOfGlobals(true); // force initialize - gROOT->GetListOfGlobalFunctions(true); // id. - std::set initial; - Cppyy::GetAllCppNames(GLOBAL_HANDLE, initial); - gInitialNames = initial; - -#ifndef WIN32 - gRootSOs.insert("libCore.so "); - gRootSOs.insert("libRIO.so "); - gRootSOs.insert("libThread.so "); - gRootSOs.insert("libMathCore.so "); -#else - gRootSOs.insert("libCore.dll "); - gRootSOs.insert("libRIO.dll "); - gRootSOs.insert("libThread.dll "); - gRootSOs.insert("libMathCore.dll "); -#endif + CppDispatch::Declare("namespace __cppyy_internal { struct Sep; }", false); // start off with a reasonable size placeholder for wrappers - gWrapperHolder.reserve(1024); + // gWrapperHolder.reserve(1024); // create an exception handler to process signals - gExceptionHandler = new TExceptionHandlerImp{}; + // gExceptionHandler = new TExceptionHandlerImp{}; } ~ApplicationStarter() { - for (auto wrap : gWrapperHolder) - delete wrap; - delete gExceptionHandler; gExceptionHandler = nullptr; + //CppDispatch::DeleteInterpreter(Interp); + // for (auto wrap : gWrapperHolder) + // delete wrap; + // delete gExceptionHandler; gExceptionHandler = nullptr; } } _applicationStarter; } // unnamed namespace -// local helpers ------------------------------------------------------------- static inline -TClassRef& type_from_handle(Cppyy::TCppScope_t scope) +char* cppstring_to_cstring(const std::string& cppstr) +{ + char* cstr = (char*)malloc(cppstr.size()+1); + memcpy(cstr, cppstr.c_str(), cppstr.size()+1); + return cstr; +} + +bool Cppyy::Compile(const std::string& code, bool silent) { - assert((ClassRefs_t::size_type)scope < g_classrefs.size()); - return g_classrefs[(ClassRefs_t::size_type)scope]; + // Declare returns an enum which equals 0 on success + return !CppDispatch::Declare(code.c_str(), silent); } -static inline -TFunction* m2f(Cppyy::TCppMethod_t method) { - CallWrapper* wrap = ((CallWrapper*)method); - if (!wrap->fTF) { - MethodInfo_t* mi = gInterpreter->MethodInfo_Factory(wrap->fDecl); - wrap->fTF = new TFunction(mi); +std::string Cppyy::ToString(TCppType_t klass, TCppObject_t obj) +{ + if (klass && obj && !CppDispatch::IsNamespace((TCppScope_t)klass)) + return CppDispatch::ObjToString(CppDispatch::GetQualifiedCompleteName(klass).c_str(), + (void*)obj); + return ""; +} + +// // name to opaque C++ scope representation ----------------------------------- +std::string Cppyy::ResolveName(const std::string& name) { + if (!name.empty()) { + if (Cppyy::TCppType_t type = + Cppyy::GetType(name, /*enable_slow_lookup=*/true)) + return Cppyy::GetTypeAsString(Cppyy::ResolveType(type)); + return name; + } + return ""; +} + +Cppyy::TCppType_t Cppyy::ResolveEnumReferenceType(TCppType_t type) { + if (!CppDispatch::IsLValueReferenceType(type)) + return type; + + TCppType_t nonReferenceType = CppDispatch::GetNonReferenceType(type); + if (CppDispatch::IsEnumType(nonReferenceType)) { + TCppType_t underlying_type = CppDispatch::GetIntegerTypeFromEnumType(nonReferenceType); + return CppDispatch::GetReferencedType(underlying_type, false); } - return wrap->fTF; + return type; } -/* -static inline -CallWrapper::DeclId_t m2d(Cppyy::TCppMethod_t method) { - CallWrapper* wrap = ((CallWrapper*)method); - if (!wrap->fTF || wrap->fTF->GetDeclId() != wrap->fDecl) { - MethodInfo_t* mi = gInterpreter->MethodInfo_Factory(wrap->fDecl); - wrap->fTF = new TFunction(mi); +Cppyy::TCppType_t Cppyy::ResolveEnumPointerType(TCppType_t type) { + if (!CppDispatch::IsPointerType(type)) + return type; + + TCppType_t PointeeType = CppDispatch::GetPointeeType(type); + if (CppDispatch::IsEnumType(PointeeType)) { + TCppType_t underlying_type = CppDispatch::GetIntegerTypeFromEnumType(PointeeType); + return CppDispatch::GetPointerType(underlying_type); } - return wrap->fDecl; + return type; } -*/ -static inline -char* cppstring_to_cstring(const std::string& cppstr) -{ - char* cstr = (char*)malloc(cppstr.size()+1); - memcpy(cstr, cppstr.c_str(), cppstr.size()+1); - return cstr; +Cppyy::TCppType_t int_like_type(Cppyy::TCppType_t type) { + Cppyy::TCppType_t check_int_typedefs = type; + if (CppDispatch::IsPointerType(check_int_typedefs)) + check_int_typedefs = CppDispatch::GetPointeeType(check_int_typedefs); + if (CppDispatch::IsReferenceType(check_int_typedefs)) + check_int_typedefs = CppDispatch::GetReferencedType(check_int_typedefs, false); + + if (CppDispatch::GetTypeAsString(check_int_typedefs) == "int8_t" || CppDispatch::GetTypeAsString(check_int_typedefs) == "uint8_t") + return check_int_typedefs; + return nullptr; } -static inline -bool match_name(const std::string& tname, const std::string fname) -{ -// either match exactly, or match the name as template - if (fname.rfind(tname, 0) == 0) { - if ((tname.size() == fname.size()) || - (tname.size() < fname.size() && fname[tname.size()] == '<')) - return true; +Cppyy::TCppType_t Cppyy::ResolveType(TCppType_t type) { + if (!type) return type; + + TCppType_t check_int_typedefs = int_like_type(type); + if (check_int_typedefs) + return type; + + Cppyy::TCppType_t canonType = CppDispatch::GetCanonicalType(type); + + if (CppDispatch::IsEnumType(canonType)) { + if (Cppyy::GetTypeAsString(type) != "std::byte") + return CppDispatch::GetIntegerTypeFromEnumType(canonType); } - return false; + if (CppDispatch::HasTypeQualifier(canonType, CppDispatch::QualKind::Restrict)) { + return CppDispatch::RemoveTypeQualifier(canonType, CppDispatch::QualKind::Restrict); + } + + return canonType; } -static inline -bool is_missclassified_stl(const std::string& name) -{ - std::string::size_type pos = name.find('<'); - if (pos != std::string::npos) - return gSTLNames.find(name.substr(0, pos)) != gSTLNames.end(); - return gSTLNames.find(name) != gSTLNames.end(); +Cppyy::TCppType_t Cppyy::GetRealType(TCppType_t type) { + TCppType_t check_int_typedefs = int_like_type(type); + if (check_int_typedefs) + return check_int_typedefs; + return CppDispatch::GetUnderlyingType(type); } +Cppyy::TCppType_t Cppyy::GetPointerType(TCppType_t type) { + return CppDispatch::GetPointerType(type); +} -// direct interpreter access ------------------------------------------------- -bool Cppyy::Compile(const std::string& code, bool /*silent*/) -{ - return gInterpreter->Declare(code.c_str()); +Cppyy::TCppType_t Cppyy::GetReferencedType(TCppType_t type, bool rvalue) { + return CppDispatch::GetReferencedType(type, rvalue); } -std::string Cppyy::ToString(TCppType_t klass, TCppObject_t obj) -{ - if (klass && obj && !IsNamespace((TCppScope_t)klass)) - return gInterpreter->ToString(GetScopedFinalName(klass).c_str(), (void*)obj); - return ""; +bool Cppyy::IsClassType(TCppType_t type) { + return CppDispatch::IsRecordType(type); } +bool Cppyy::IsPointerType(TCppType_t type) { + return CppDispatch::IsPointerType(type); +} -// name to opaque C++ scope representation ----------------------------------- -std::string Cppyy::ResolveName(const std::string& cppitem_name) -{ -// Fully resolve the given name to the final type name. +bool Cppyy::IsFunctionPointerType(TCppType_t type) { + return CppDispatch::IsFunctionPointerType(type); +} -// try memoized type cache, in case seen before - std::string memoized = find_memoized_resolved_name(cppitem_name); - if (!memoized.empty()) return memoized; +std::string trim(const std::string& line) +{ + if (line.empty()) return ""; + const char* WhiteSpace = " \t\v\r\n"; + std::size_t start = line.find_first_not_of(WhiteSpace); + std::size_t end = line.find_last_not_of(WhiteSpace); + return line.substr(start, end - start + 1); +} -// remove global scope '::' if present - std::string tclean = cppitem_name.compare(0, 2, "::") == 0 ? - cppitem_name.substr(2, std::string::npos) : cppitem_name; +// returns false of angular brackets dont match, else true +bool split_comma_saparated_types(const std::string& name, + std::vector& types) { + std::string trimed_name = trim(name); + size_t start_pos = 0; + size_t end_pos = 0; + size_t appended_count = 0; + int matching_angular_brackets = 0; + while (end_pos < trimed_name.size()) { + switch (trimed_name[end_pos]) { + case ',': { + if (!matching_angular_brackets) { + types.push_back( + trim(trimed_name.substr(start_pos, end_pos - start_pos))); + start_pos = end_pos + 1; + } + break; + } + case '<': { + matching_angular_brackets++; + break; + } + case '>': { + if (matching_angular_brackets > 0) { + types.push_back( + trim(trimed_name.substr(start_pos, end_pos - start_pos + 1))); + start_pos = end_pos + 1; + } else if (matching_angular_brackets < 1) { + types.clear(); + return false; + } + start_pos++; + end_pos++; + matching_angular_brackets--; + break; + } + } + end_pos++; + } + if (start_pos < trimed_name.size()) + types.push_back(trim(trimed_name.substr(start_pos, end_pos - start_pos))); + return true; +} -// classes (most common) - tclean = TClassEdit::CleanType(tclean.c_str()); - if (tclean.empty() /* unknown, eg. an operator */) return cppitem_name; +// returns true if no new type was added. +bool Cppyy::AppendTypesSlow(const std::string& name, + std::vector& types, Cppyy::TCppScope_t parent) { -// reduce [N] to [] - if (tclean[tclean.size()-1] == ']') - tclean = tclean.substr(0, tclean.rfind('[')) + "[]"; + // Add no new type if string is empty + if (name.empty()) + return true; - if (tclean.rfind("byte", 0) == 0 || tclean.rfind("std::byte", 0) == 0) - return tclean; + auto replace_all = [](std::string& str, const std::string& from, const std::string& to) { + if(from.empty()) + return; + size_t start_pos = 0; + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } + }; + + std::string resolved_name = name; + replace_all(resolved_name, "std::initializer_list<", "std::vector<"); // replace initializer_list with vector + + // We might have an entire expression such as int, double. + static unsigned long long struct_count = 0; + std::string code = "template struct __Cppyy_AppendTypesSlow {};\n"; + if (!struct_count) + CppDispatch::Declare(code.c_str(), false); // initialize the trampoline + + std::string var = "__Cppyy_s" + std::to_string(struct_count++); + // FIXME: We cannot use silent because it erases our error code from Declare! + if (!CppDispatch::Declare(("__Cppyy_AppendTypesSlow<" + resolved_name + "> " + var +";\n").c_str(), /*silent=*/false)) { + TCppType_t varN = CppDispatch::GetVariableType(CppDispatch::GetNamed(var.c_str(), nullptr)); + TCppScope_t instance_class = CppDispatch::GetScopeFromType(varN); + size_t oldSize = types.size(); + CppDispatch::GetClassTemplateInstantiationArgs(instance_class, types); + return oldSize == types.size(); + } + + // We split each individual types based on , and resolve it + // FIXME: see discussion on should we support template instantiation with string: + // https://github.com/compiler-research/cppyy-backend/pull/137#discussion_r2079357491 + // We should consider eliminating the `split_comma_saparated_types` and `is_integral` + // string parsing. + std::vector individual_types; + if (!split_comma_saparated_types(resolved_name, individual_types)) + return true; -// remove __restrict and __restrict__ - auto pos = tclean.rfind("__restrict"); - if (pos != std::string::npos) - tclean = tclean.substr(0, pos); + for (std::string& i : individual_types) { + // Try going via Cppyy::GetType first. + const char* integral_value = nullptr; + Cppyy::TCppType_t type = nullptr; - if (tclean.compare(0, 9, "std::byte") == 0) - return tclean; + type = GetType(i, /*enable_slow_lookup=*/true); + if (!type && parent && (CppDispatch::IsNamespace(parent) || CppDispatch::IsClass(parent))) { + type = Cppyy::GetTypeFromScope(Cppyy::GetNamed(resolved_name, parent)); + } -// check data types list (accept only builtins as typedefs will -// otherwise not be resolved) - if (IsBuiltin(tclean)) return tclean; + if (!type) { + types.clear(); + return true; + } -// special case for enums - if (IsEnum(cppitem_name)) - return ResolveEnum(cppitem_name); + if (is_integral(i)) + integral_value = strdup(i.c_str()); + types.emplace_back(type, integral_value); + } + return false; +} -// special case for clang's builtin __type_pack_element (which does not resolve) - pos = cppitem_name.size() > 20 ? \ - cppitem_name.rfind("__type_pack_element", 5) : std::string::npos; - if (pos != std::string::npos) { - // shape is "[std::]__type_pack_elementcpd": extract - // first the index, and from there the indexed type; finally, restore the - // qualifiers - const char* str = cppitem_name.c_str(); - char* endptr = nullptr; - unsigned long index = strtoul(str+20+pos, &endptr, 0); +Cppyy::TCppType_t Cppyy::GetType(const std::string &name, bool enable_slow_lookup /* = false */) { + static unsigned long long var_count = 0; - std::string tmplvars{endptr}; - auto start = tmplvars.find(',') + 1; - auto end = tmplvars.find(',', start); - while (index != 0) { - start = end+1; - end = tmplvars.find(',', start); - if (end == std::string::npos) end = tmplvars.rfind('>'); - --index; - } + if (auto type = CppDispatch::GetType(name)) + return type; - std::string resolved = tmplvars.substr(start, end-start); - auto cpd = tmplvars.rfind('>'); - if (cpd != std::string::npos && cpd+1 != tmplvars.size()) - return resolved + tmplvars.substr(cpd+1, std::string::npos); - return resolved; + if (!enable_slow_lookup) { + if (name.find("::") != std::string::npos) + throw std::runtime_error("Calling Cppyy::GetType with qualified name '" + + name + "'\n"); + return nullptr; } -// typedefs etc. (and a couple of hacks around TClassEdit-isms, fixing of which -// in ResolveTypedef itself is a TODO ...) - tclean = TClassEdit::ResolveTypedef(tclean.c_str(), true); - pos = 0; - while ((pos = tclean.find("::::", pos)) != std::string::npos) { - tclean.replace(pos, 4, "::"); - pos += 2; + // Here we might need to deal with integral types such as 3.14. + + std::string id = "__Cppyy_GetType_" + std::to_string(var_count++); + std::string using_clause = "using " + id + " = __typeof__(" + name + ");\n"; + + if (!CppDispatch::Declare(using_clause.c_str(), /*silent=*/false)) { + TCppScope_t lookup = CppDispatch::GetNamed(id, 0); + TCppType_t lookup_ty = CppDispatch::GetTypeFromScope(lookup); + return CppDispatch::GetCanonicalType(lookup_ty); } + return nullptr; +} - if (tclean.compare(0, 6, "const ") != 0) - return TClassEdit::ShortType(tclean.c_str(), 2); - return "const " + TClassEdit::ShortType(tclean.c_str(), 2); + +Cppyy::TCppType_t Cppyy::GetComplexType(const std::string &name) { + return CppDispatch::GetComplexType(CppDispatch::GetType(name)); } -#if 0 -//---------------------------------------------------------------------------- -static std::string extract_namespace(const std::string& name) -{ -// Find the namespace the named class lives in, take care of templates -// Note: this code also lives in CPyCppyy (TODO: refactor?) - if (name.empty()) - return name; - - int tpl_open = 0; - for (std::string::size_type pos = name.size()-1; 0 < pos; --pos) { - std::string::value_type c = name[pos]; - - // count '<' and '>' to be able to skip template contents - if (c == '>') - ++tpl_open; - else if (c == '<') - --tpl_open; - - // collect name up to "::" - else if (tpl_open == 0 && c == ':' && name[pos-1] == ':') { - // found the extend of the scope ... done - return name.substr(0, pos-1); - } - } +std::string Cppyy::ResolveEnum(TCppScope_t handle) +{ + std::string type = CppDispatch::GetTypeAsString( + CppDispatch::GetIntegerTypeFromEnumScope(handle)); + if (type == "signed char") + return "char"; + return type; +} -// no namespace; assume outer scope - return ""; +Cppyy::TCppScope_t Cppyy::GetUnderlyingScope(TCppScope_t scope) +{ + return CppDispatch::GetUnderlyingScope(scope); } -#endif -std::string Cppyy::ResolveEnum(const std::string& enum_type) -{ -// The underlying type of a an enum may be any kind of integer. -// Resolve that type via a workaround (note: this function assumes -// that the enum_type name is a valid enum type name) - auto res = resolved_enum_types.find(enum_type); - if (res != resolved_enum_types.end()) - return res->second; - -// desugar the type before resolving - std::string et_short = TClassEdit::ShortType(enum_type.c_str(), 1); - if (et_short.find("(unnamed") == std::string::npos) { - std::ostringstream decl; - // TODO: now presumed fixed with https://sft.its.cern.ch/jira/browse/ROOT-6988 - for (auto& itype : {"unsigned int"}) { - decl << "std::is_same<" - << itype - << ", std::underlying_type<" - << et_short - << ">::type>::value;"; - if (gInterpreter->ProcessLine(decl.str().c_str())) { - // TODO: "re-sugaring" like this is brittle, but the top - // should be re-translated into AST-based code anyway - std::string resugared; - if (et_short.size() != enum_type.size()) { - auto pos = enum_type.find(et_short); - if (pos != std::string::npos) { - resugared = enum_type.substr(0, pos) + itype; - if (pos+et_short.size() < enum_type.size()) - resugared += enum_type.substr(pos+et_short.size(), std::string::npos); - } - } - if (resugared.empty()) resugared = itype; - resolved_enum_types[enum_type] = resugared; - return resugared; - } - } - } +Cppyy::TCppScope_t Cppyy::GetScope(const std::string& name, + TCppScope_t parent_scope) +{ + if (Cppyy::TCppScope_t scope = CppDispatch::GetScope(name, parent_scope)) + return scope; + if (!parent_scope || parent_scope == CppDispatch::GetGlobalScope()) + if (Cppyy::TCppScope_t scope = CppDispatch::GetScopeFromCompleteName(name)) + return scope; + + // FIXME: avoid string parsing here + if (name.find('<') != std::string::npos) { + // Templated Type; May need instantiation + size_t start = name.find('<'); + size_t end = name.rfind('>'); + std::string params = name.substr(start + 1, end - start - 1); -// failed or anonymous ... signal upstream to special case this - int ipos = (int)enum_type.size()-1; - for (; 0 <= ipos; --ipos) { - char c = enum_type[ipos]; - if (isspace(c)) continue; - if (isalnum(c) || c == '_' || c == '>' || c == ')') break; + std::string pure_name = name.substr(0, start); + Cppyy::TCppScope_t scope = CppDispatch::GetScope(pure_name, parent_scope); + if (!scope && (!parent_scope || parent_scope == CppDispatch::GetGlobalScope())) + scope = CppDispatch::GetScopeFromCompleteName(pure_name); + + if (Cppyy::IsTemplate(scope)) { + std::vector templ_params; + if (!Cppyy::AppendTypesSlow(params, templ_params)) + return CppDispatch::InstantiateTemplate(scope, templ_params.data(), + templ_params.size(), false); + } } - bool isConst = enum_type.find("const ", 6) != std::string::npos; - std::string restype = isConst ? "const " : ""; - restype += "internal_enum_type_t"+enum_type.substr((std::string::size_type)ipos+1, std::string::npos); - resolved_enum_types[enum_type] = restype; - return restype; // should default to some int variant + return nullptr; } -#if 0 -static Cppyy::TCppIndex_t ArgSimilarityScore(void *argqtp, void *reqqtp) -{ -// This scoring is not based on any particular rules - if (gInterpreter->IsSameType(argqtp, reqqtp)) - return 0; // Best match - else if ((gInterpreter->IsSignedIntegerType(argqtp) && gInterpreter->IsSignedIntegerType(reqqtp)) || - (gInterpreter->IsUnsignedIntegerType(argqtp) && gInterpreter->IsUnsignedIntegerType(reqqtp)) || - (gInterpreter->IsFloatingType(argqtp) && gInterpreter->IsFloatingType(reqqtp))) - return 1; - else if ((gInterpreter->IsSignedIntegerType(argqtp) && gInterpreter->IsUnsignedIntegerType(reqqtp)) || - (gInterpreter->IsFloatingType(argqtp) && gInterpreter->IsUnsignedIntegerType(reqqtp))) - return 2; - else if ((gInterpreter->IsIntegerType(argqtp) && gInterpreter->IsIntegerType(reqqtp))) - return 3; - else if ((gInterpreter->IsIntegralType(argqtp) && gInterpreter->IsIntegralType(reqqtp))) - return 4; - else if ((gInterpreter->IsVoidPointerType(argqtp) && gInterpreter->IsPointerType(reqqtp))) - return 5; - else - return 10; // Penalize heavily for no possible match +Cppyy::TCppScope_t Cppyy::GetFullScope(const std::string& name) +{ + return Cppyy::GetScope(name); } -#endif -Cppyy::TCppScope_t Cppyy::GetScope(const std::string& sname) -{ -// First, try cache - TCppType_t result = find_memoized_scope(sname); - if (result) return result; - -// Second, skip builtins before going through the more expensive steps of resolving -// typedefs and looking up TClass - if (g_builtins.find(sname) != g_builtins.end()) - return (TCppScope_t)0; - -// TODO: scope_name should always be final already? -// Resolve name fully before lookup to make sure all aliases point to the same scope - std::string scope_name = ResolveName(sname); - bool bHasAlias1 = sname != scope_name; - if (bHasAlias1) { - result = find_memoized_scope(scope_name); - if (result) { - g_name2classrefidx[sname] = result; - return result; - } - } +Cppyy::TCppScope_t Cppyy::GetTypeScope(TCppScope_t var) +{ + return CppDispatch::GetScopeFromType( + CppDispatch::GetVariableType(var)); +} -// both failed, but may be STL name that's missing 'std::' now, but didn't before - bool b_scope_name_missclassified = is_missclassified_stl(scope_name); - if (b_scope_name_missclassified) { - result = find_memoized_scope("std::"+scope_name); - if (result) g_name2classrefidx["std::"+scope_name] = (ClassRefs_t::size_type)result; - } - bool b_sname_missclassified = bHasAlias1 ? is_missclassified_stl(sname) : false; - if (b_sname_missclassified) { - if (!result) result = find_memoized_scope("std::"+sname); - if (result) g_name2classrefidx["std::"+sname] = (ClassRefs_t::size_type)result; - } +Cppyy::TCppScope_t Cppyy::GetNamed(const std::string& name, + TCppScope_t parent_scope) +{ + return CppDispatch::GetNamed(name, parent_scope); +} - if (result) return result; - -// use TClass directly, to enable auto-loading; class may be stubbed (eg. for -// function returns) or forward declared, leading to a non-null TClass that is -// otherwise invalid/unusable - TClassRef cr(TClass::GetClass(scope_name.c_str(), true /* load */, true /* silent */)); - if (!cr.GetClass()) - return (TCppScope_t)0; - -// memoize found/created TClass - bool bHasAlias2 = cr->GetName() != scope_name; - if (bHasAlias2) { - result = find_memoized_scope(cr->GetName()); - if (result) { - g_name2classrefidx[scope_name] = result; - if (bHasAlias1) g_name2classrefidx[sname] = result; - return result; - } - } +Cppyy::TCppScope_t Cppyy::GetParentScope(TCppScope_t scope) +{ + return CppDispatch::GetParentScope(scope); +} - ClassRefs_t::size_type sz = g_classrefs.size(); - g_name2classrefidx[scope_name] = sz; - if (bHasAlias1) g_name2classrefidx[sname] = sz; - if (bHasAlias2) g_name2classrefidx[cr->GetName()] = sz; -// TODO: make ROOT/meta NOT remove std :/ - if (b_scope_name_missclassified) - g_name2classrefidx["std::"+scope_name] = sz; - if (b_sname_missclassified) - g_name2classrefidx["std::"+sname] = sz; +Cppyy::TCppScope_t Cppyy::GetScopeFromType(TCppType_t type) +{ + return CppDispatch::GetScopeFromType(type); +} - g_classrefs.push_back(TClassRef(scope_name.c_str())); +Cppyy::TCppType_t Cppyy::GetTypeFromScope(TCppScope_t klass) +{ + return CppDispatch::GetTypeFromScope(klass); +} - return (TCppScope_t)sz; +Cppyy::TCppScope_t Cppyy::GetGlobalScope() +{ + return CppDispatch::GetGlobalScope(); } -bool Cppyy::IsTemplate(const std::string& template_name) +bool Cppyy::IsTemplate(TCppScope_t handle) { - return (bool)gInterpreter->CheckClassTemplate(template_name.c_str()); + return CppDispatch::IsTemplate(handle); } -namespace { - class AutoCastRTTI { - public: - virtual ~AutoCastRTTI() {} - }; +bool Cppyy::IsTemplateInstantiation(TCppScope_t handle) +{ + return CppDispatch::IsTemplateSpecialization(handle); } -Cppyy::TCppType_t Cppyy::GetActualClass(TCppType_t klass, TCppObject_t obj) +bool Cppyy::IsTypedefed(TCppScope_t handle) { - TClassRef& cr = type_from_handle(klass); - if (!cr.GetClass() || !obj) return klass; + return CppDispatch::IsTypedefed(handle); +} - if (!(cr->ClassProperty() & kClassHasVirtual)) - return klass; // not polymorphic: no RTTI info available +namespace { +class AutoCastRTTI { +public: + virtual ~AutoCastRTTI() {} +}; +} // namespace -// TODO: ios class casting (ostream, streambuf, etc.) fails with a crash in GetActualClass() -// below on Mac ARM (it's likely that the found actual class was replaced, maybe because -// there are duplicates from pcm/pch?); filter them out for now as it's usually unnecessary -// anyway to autocast these - std::string clName = cr->GetName(); - if (clName.find("std::", 0, 5) == 0 && clName.find("stream") != std::string::npos) +Cppyy::TCppScope_t Cppyy::GetActualClass(TCppScope_t klass, TCppObject_t obj) { + if (!CppDispatch::IsClassPolymorphic(klass)) return klass; -#ifdef _WIN64 -// Cling does not provide a consistent ImageBase address for calculating relative addresses -// as used in Windows 64b RTTI. So, check for our own RTTI extension instead. If that fails, -// see whether the unmangled raw_name is available (e.g. if this is an MSVC compiled rather -// than JITed class) and pass on if it is. - volatile const char* raw = nullptr; // to prevent too aggressive reordering - try { - // this will filter those objects that do not have RTTI to begin with (throws) - AutoCastRTTI* pcst = (AutoCastRTTI*)obj; - raw = typeid(*pcst).raw_name(); - - // check the signature id (0 == absolute, 1 == relative, 2 == ours) - void* vfptr = *(void**)((intptr_t)obj); - void* meta = (void*)((intptr_t)*((void**)((intptr_t)vfptr-sizeof(void*)))); - if (*(intptr_t*)meta == 2) { - // access the extra data item which is an absolute pointer to the RTTI - void* ptdescr = (void*)((intptr_t)meta + 4*sizeof(unsigned long)+sizeof(void*)); - if (ptdescr && *(void**)ptdescr) { - auto rtti = *(std::type_info**)ptdescr; - raw = rtti->raw_name(); - if (raw && raw[0] != '\0') // likely unnecessary - return (TCppType_t)GetScope(rtti->name()); - } - - return klass; // do not fall through if no RTTI info available - } + const std::type_info *typ = &typeid(*(AutoCastRTTI *)obj); - // if the raw name is the empty string (no guarantees that this is so as truly, the - // address is corrupt, but it is common to be empty), then there is no accessible RTTI - // and getting the unmangled name will crash ... - if (!raw) - return klass; - } catch (std::bad_typeid) { - return klass; // can't risk passing to ROOT/meta as it may do RTTI - } -#endif + std::string mangled_name = typ->name(); + std::string demangled_name = CppDispatch::Demangle(mangled_name); - TClass* clActual = cr->GetActualClass((void*)obj); - // The additional check using TClass::GetClassInfo is to prevent returning classes of which the Interpreter has no info (see https://github.com/root-project/root/pull/16177) - if (clActual && clActual != cr.GetClass() && clActual->GetClassInfo()) { - auto itt = g_name2classrefidx.find(clActual->GetName()); - if (itt != g_name2classrefidx.end()) - return (TCppType_t)itt->second; - return (TCppType_t)GetScope(clActual->GetName()); - } + if (TCppScope_t scope = Cppyy::GetScope(demangled_name)) + return scope; return klass; } -size_t Cppyy::SizeOf(TCppType_t klass) +size_t Cppyy::SizeOf(TCppScope_t klass) { - TClassRef& cr = type_from_handle(klass); - if (cr.GetClass() && cr->GetClassInfo()) - return (size_t)gInterpreter->ClassInfo_Size(cr->GetClassInfo()); - return (size_t)0; + return CppDispatch::SizeOf(klass); } -size_t Cppyy::SizeOf(const std::string& type_name) +size_t Cppyy::SizeOfType(TCppType_t klass) { - TDataType* dt = gROOT->GetType(type_name.c_str()); - if (dt) return dt->Size(); - return SizeOf(GetScope(type_name)); + return CppDispatch::GetSizeOfType(klass); } bool Cppyy::IsBuiltin(const std::string& type_name) { - if (g_builtins.find(type_name) != g_builtins.end()) - return true; - - const std::string& tclean = TClassEdit::CleanType(type_name.c_str(), 1); - if (g_builtins.find(tclean) != g_builtins.end()) - return true; + static std::set s_builtins = + {"bool", "char", "signed char", "unsigned char", "wchar_t", "short", + "unsigned short", "int", "unsigned int", "long", "unsigned long", + "long long", "unsigned long long", "float", "double", "long double", + "void"}; + if (s_builtins.find(trim(type_name)) != s_builtins.end()) + return true; - if (strstr(tclean.c_str(), "std::complex")) + if (strstr(type_name.c_str(), "std::complex")) return true; return false; } -bool Cppyy::IsComplete(const std::string& type_name) +bool Cppyy::IsBuiltin(TCppType_t type) { -// verify whether the dictionary of this class is fully available - bool b = false; - - int oldEIL = gErrorIgnoreLevel; - gErrorIgnoreLevel = 3000; - TClass* klass = TClass::GetClass(type_name.c_str()); - if (klass && klass->GetClassInfo()) // works for normal case w/ dict - b = gInterpreter->ClassInfo_IsLoaded(klass->GetClassInfo()); - else { // special case for forward declared classes - ClassInfo_t* ci = gInterpreter->ClassInfo_Factory(type_name.c_str()); - if (ci) { - b = gInterpreter->ClassInfo_IsLoaded(ci); - gInterpreter->ClassInfo_Delete(ci); // we own the fresh class info - } - } - gErrorIgnoreLevel = oldEIL; - return b; + return CppDispatch::IsBuiltin(type); + } -// memory management --------------------------------------------------------- -Cppyy::TCppObject_t Cppyy::Allocate(TCppType_t type) +bool Cppyy::IsComplete(TCppScope_t scope) { - TClassRef& cr = type_from_handle(type); - return (TCppObject_t)::operator new(gInterpreter->ClassInfo_Size(cr->GetClassInfo())); + return CppDispatch::IsComplete(scope); } -void Cppyy::Deallocate(TCppType_t /* type */, TCppObject_t instance) +// // memory management --------------------------------------------------------- +Cppyy::TCppObject_t Cppyy::Allocate(TCppScope_t scope) { - ::operator delete(instance); + return CppDispatch::Allocate(scope, 1UL); } -Cppyy::TCppObject_t Cppyy::Construct(TCppType_t type, void* arena) +void Cppyy::Deallocate(TCppScope_t scope, TCppObject_t instance) { - TClassRef& cr = type_from_handle(type); - if (arena) - return (TCppObject_t)cr->New(arena, TClass::kRealNew); - return (TCppObject_t)cr->New(TClass::kRealNew); + CppDispatch::Deallocate(scope, instance, 1UL); } -static std::map sHasOperatorDelete; -void Cppyy::Destruct(TCppType_t type, TCppObject_t instance) +Cppyy::TCppObject_t Cppyy::Construct(TCppScope_t scope, void* arena/*=nullptr*/) { - TClassRef& cr = type_from_handle(type); - if (cr->ClassProperty() & (kClassHasExplicitDtor | kClassHasImplicitDtor)) - cr->Destructor((void*)instance); - else { - ROOT::DelFunc_t fdel = cr->GetDelete(); - if (fdel) fdel((void*)instance); - else { - auto ib = sHasOperatorDelete.find(type); - if (ib == sHasOperatorDelete.end()) { - TFunction *f = (TFunction *)cr->GetMethodAllAny("operator delete"); - sHasOperatorDelete[type] = (bool)(f && (f->Property() & kIsPublic)); - ib = sHasOperatorDelete.find(type); - } - ib->second ? cr->Destructor((void*)instance) : ::operator delete((void*)instance); - } - } + return CppDispatch::Construct(scope, arena, 1UL); } - -// method/function dispatching ----------------------------------------------- -static TInterpreter::CallFuncIFacePtr_t GetCallFunc(Cppyy::TCppMethod_t method) +void Cppyy::Destruct(TCppScope_t scope, TCppObject_t instance) { -// TODO: method should be a callfunc, so that no mapping would be needed. - CallWrapper* wrap = (CallWrapper*)method; - - CallFunc_t* callf = gInterpreter->CallFunc_Factory(); - MethodInfo_t* meth = gInterpreter->MethodInfo_Factory(wrap->fDecl); - gInterpreter->CallFunc_SetFunc(callf, meth); - gInterpreter->MethodInfo_Delete(meth); - - if (!(callf && gInterpreter->CallFunc_IsValid(callf))) { - // TODO: propagate this error to caller w/o use of Python C-API - /* - PyErr_Format(PyExc_RuntimeError, "could not resolve %s::%s(%s)", - const_cast(klass).GetClassName(), - wrap.fName, callString.c_str()); */ - std::cerr << "TODO: report unresolved function error to Python\n"; - if (callf) gInterpreter->CallFunc_Delete(callf); - return TInterpreter::CallFuncIFacePtr_t{}; - } - -// generate the wrapper and JIT it; ignore wrapper generation errors (will simply -// result in a nullptr that is reported upstream if necessary; often, however, -// there is a different overload available that will do) - auto oldErrLvl = gErrorIgnoreLevel; - gErrorIgnoreLevel = kFatal; - wrap->fFaceptr = gInterpreter->CallFunc_IFacePtr(callf); - gErrorIgnoreLevel = oldErrLvl; - - gInterpreter->CallFunc_Delete(callf); // does not touch IFacePtr - return wrap->fFaceptr; + CppDispatch::Destruct(instance, scope, true, 0UL); } static inline @@ -981,60 +910,43 @@ bool copy_args(Parameter* args, size_t nargs, void** vargs) } static inline -void release_args(Parameter* args, size_t nargs) -{ +void release_args(Parameter* args, size_t nargs) { for (size_t i = 0; i < nargs; ++i) { if (args[i].fTypeCode == 'X') free(args[i].fValue.fVoidp); } } -static inline bool WrapperCall(Cppyy::TCppMethod_t method, size_t nargs, void* args_, void* self, void* result) +static inline +bool WrapperCall(Cppyy::TCppMethod_t method, size_t nargs, void* args_, void* self, void* result) { Parameter* args = (Parameter*)args_; - //bool is_direct = nargs & DIRECT_CALL; + bool is_direct = nargs & DIRECT_CALL; nargs = CALL_NARGS(nargs); - CallWrapper* wrap = (CallWrapper*)method; - const TInterpreter::CallFuncIFacePtr_t& faceptr = wrap->fFaceptr.fGeneric ? wrap->fFaceptr : GetCallFunc(method); - if (!faceptr.fGeneric) - return false; // happens with compilation error + // if (!is_ready(wrap, is_direct)) + // return false; // happens with compilation error - if (faceptr.fKind == TInterpreter::CallFuncIFacePtr_t::kGeneric) { + if (CppDispatch::JitCall JC = CppDispatch::MakeFunctionCallable(method)) { bool runRelease = false; + //const auto& fgen = /* is_direct ? faceptr.fDirect : */ faceptr; if (nargs <= SMALL_ARGS_N) { void* smallbuf[SMALL_ARGS_N]; if (nargs) runRelease = copy_args(args, nargs, smallbuf); - faceptr.fGeneric(self, (int)nargs, smallbuf, result); - } else { - std::vector buf(nargs); - runRelease = copy_args(args, nargs, buf.data()); - faceptr.fGeneric(self, (int)nargs, buf.data(), result); - } - if (runRelease) release_args(args, nargs); - return true; - } - - if (faceptr.fKind == TInterpreter::CallFuncIFacePtr_t::kCtor) { - bool runRelease = false; - if (nargs <= SMALL_ARGS_N) { - void* smallbuf[SMALL_ARGS_N]; - if (nargs) runRelease = copy_args(args, nargs, (void**)smallbuf); - faceptr.fCtor((void**)smallbuf, result, (unsigned long)nargs); + // CLING_CATCH_UNCAUGHT_ + JC.Invoke(result, {smallbuf, nargs}, self); + // _CLING_CATCH_UNCAUGHT } else { std::vector buf(nargs); runRelease = copy_args(args, nargs, buf.data()); - faceptr.fCtor(buf.data(), result, (unsigned long)nargs); + // CLING_CATCH_UNCAUGHT_ + JC.Invoke(result, {buf.data(), nargs}, self); + // _CLING_CATCH_UNCAUGHT } if (runRelease) release_args(args, nargs); return true; } - if (faceptr.fKind == TInterpreter::CallFuncIFacePtr_t::kDtor) { - std::cerr << " DESTRUCTOR NOT IMPLEMENTED YET! " << std::endl; - return false; - } - return false; } @@ -1045,12 +957,21 @@ T CallT(Cppyy::TCppMethod_t method, Cppyy::TCppObject_t self, size_t nargs, void T t{}; if (WrapperCall(method, nargs, args, (void*)self, &t)) return t; + throw std::runtime_error("failed to resolve function"); return (T)-1; } +#ifdef PRINT_DEBUG + #define _IMP_CALL_PRINT_STMT(type) \ + printf("IMP CALL with type: %s\n", #type); +#else + #define _IMP_CALL_PRINT_STMT(type) +#endif + #define CPPYY_IMP_CALL(typecode, rtype) \ rtype Cppyy::Call##typecode(TCppMethod_t method, TCppObject_t self, size_t nargs, void* args)\ { \ + _IMP_CALL_PRINT_STMT(rtype) \ return CallT(method, self, nargs, args); \ } @@ -1065,10 +986,10 @@ CPPYY_IMP_CALL(C, char ) CPPYY_IMP_CALL(H, short ) CPPYY_IMP_CALL(I, int ) CPPYY_IMP_CALL(L, long ) -CPPYY_IMP_CALL(LL, Long64_t ) +CPPYY_IMP_CALL(LL, long long ) CPPYY_IMP_CALL(F, float ) CPPYY_IMP_CALL(D, double ) -CPPYY_IMP_CALL(LD, LongDouble_t ) +CPPYY_IMP_CALL(LD, long double ) void* Cppyy::CallR(TCppMethod_t method, TCppObject_t self, size_t nargs, void* args) { @@ -1082,7 +1003,7 @@ char* Cppyy::CallS( TCppMethod_t method, TCppObject_t self, size_t nargs, void* args, size_t* length) { char* cstr = nullptr; - TClassRef cr("std::string"); + // TClassRef cr("std::string"); // TODO: Why is this required? std::string* cppresult = (std::string*)malloc(sizeof(std::string)); if (WrapperCall(method, nargs, args, self, (void*)cppresult)) { cstr = cppstring_to_cstring(*cppresult); @@ -1095,25 +1016,22 @@ char* Cppyy::CallS( } Cppyy::TCppObject_t Cppyy::CallConstructor( - TCppMethod_t method, TCppType_t /* klass */, size_t nargs, void* args) + TCppMethod_t method, TCppScope_t klass, size_t nargs, void* args) { void* obj = nullptr; - if (WrapperCall(method, nargs, args, nullptr, &obj)) - return (TCppObject_t)obj; - return (TCppObject_t)0; + WrapperCall(method, nargs, args, nullptr, &obj); + return (TCppObject_t)obj; } -void Cppyy::CallDestructor(TCppType_t type, TCppObject_t self) +void Cppyy::CallDestructor(TCppScope_t scope, TCppObject_t self) { - TClassRef& cr = type_from_handle(type); - cr->Destructor((void*)self, true); + CppDispatch::Destruct(self, scope, /*withFree=*/false, 0UL); } Cppyy::TCppObject_t Cppyy::CallO(TCppMethod_t method, TCppObject_t self, size_t nargs, void* args, TCppType_t result_type) { - TClassRef& cr = type_from_handle(result_type); - void* obj = ::operator new(gInterpreter->ClassInfo_Size(cr->GetClassInfo())); + void* obj = ::operator new(CppDispatch::GetSizeOfType(result_type)); if (WrapperCall(method, nargs, args, self, obj)) return (TCppObject_t)obj; ::operator delete(obj); @@ -1122,54 +1040,7 @@ Cppyy::TCppObject_t Cppyy::CallO(TCppMethod_t method, Cppyy::TCppFuncAddr_t Cppyy::GetFunctionAddress(TCppMethod_t method, bool check_enabled) { - if (check_enabled && !gEnableFastPath) return (TCppFuncAddr_t)nullptr; - TFunction* f = m2f(method); - - TCppFuncAddr_t pf = (TCppFuncAddr_t)gInterpreter->FindSym(f->GetMangledName()); - if (pf) return pf; - - int ierr = 0; - const char* fn = TClassEdit::DemangleName(f->GetMangledName(), ierr); - if (ierr || !fn) - return pf; - - // TODO: the following attempts are all brittle and leak transactions, but - // each properly exposes the symbol so subsequent lookups will succeed - if (strstr(f->GetName(), "<")) { - // force explicit instantiation and try again - std::ostringstream sig; - sig << "template " << fn << ";"; - gInterpreter->ProcessLine(sig.str().c_str()); - } else { - std::ostringstream sig; - - std::string sfn = fn; - std::string::size_type pos = sfn.find('('); - if (pos != std::string::npos) sfn = sfn.substr(0, pos); - - // start cast - sig << '(' << f->GetReturnTypeName() << " ("; - - // add scope for methods - pos = sfn.rfind(':'); - if (pos != std::string::npos) { - std::string scope_name = sfn.substr(0, pos-1); - TCppScope_t scope = GetScope(scope_name); - if (scope && !IsNamespace(scope)) - sig << scope_name << "::"; - } - - // finalize cast - sig << "*)" << GetMethodSignature(method, false) - << ((f->Property() & kIsConstMethod) ? " const" : "") - << ')'; - - // load address - sig << '&' << sfn; - gInterpreter->Calc(sig.str().c_str()); - } - - return (TCppFuncAddr_t)gInterpreter->FindSym(f->GetMangledName()); + return (TCppFuncAddr_t) CppDispatch::GetFunctionAddress(method); } @@ -1198,339 +1069,94 @@ size_t Cppyy::GetFunctionArgTypeoffset() // scope reflection information ---------------------------------------------- bool Cppyy::IsNamespace(TCppScope_t scope) { -// Test if this scope represents a namespace. - if (scope == GLOBAL_HANDLE) - return true; - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) - return cr->Property() & kIsNamespace; - return false; + if (!scope) + return false; + + // Test if this scope represents a namespace. + return CppDispatch::IsNamespace(scope) || CppDispatch::GetGlobalScope() == scope; } -bool Cppyy::IsAbstract(TCppType_t klass) +bool Cppyy::IsClass(TCppScope_t scope) { -// Test if this type may not be instantiated. - TClassRef& cr = type_from_handle(klass); - if (cr.GetClass()) - return cr->Property() & kIsAbstract; - return false; + // Test if this scope represents a namespace. + return CppDispatch::IsClass(scope); +} +// +bool Cppyy::IsAbstract(TCppScope_t scope) +{ + // Test if this type may not be instantiated. + return CppDispatch::IsAbstract(scope); } -bool Cppyy::IsEnum(const std::string& type_name) +bool Cppyy::IsEnumScope(TCppScope_t scope) { - if (type_name.empty()) return false; + return CppDispatch::IsEnumScope(scope); +} - if (type_name.rfind("enum ", 0) == 0) - return true; // by definition (C-style) +bool Cppyy::IsEnumConstant(TCppScope_t scope) +{ + return CppDispatch::IsEnumConstant(CppDispatch::GetUnderlyingScope(scope)); +} - std::string tn_short = TClassEdit::ShortType(type_name.c_str(), 1); - if (tn_short.empty()) return false; - return gInterpreter->ClassInfo_IsEnum(tn_short.c_str()); +bool Cppyy::IsEnumType(TCppType_t type) +{ + return CppDispatch::IsEnumType(type); } bool Cppyy::IsAggregate(TCppType_t type) { -// Test if this type is a "plain old data" type - TClassRef& cr = type_from_handle(type); - if (cr.GetClass()) - return cr->ClassProperty() & kClassIsAggregate; - return false; + // Test if this type is a "plain old data" type + return CppDispatch::IsAggregate(type); } -bool Cppyy::IsDefaultConstructable(TCppType_t type) +bool Cppyy::IsDefaultConstructable(TCppScope_t scope) { // Test if this type has a default constructor or is a "plain old data" type - TClassRef& cr = type_from_handle(type); - if (cr.GetClass()) - return cr->HasDefaultConstructor() || (cr->ClassProperty() & kClassIsAggregate); - return true; + return CppDispatch::HasDefaultConstructor(scope); } -// helpers for stripping scope names -static -std::string outer_with_template(const std::string& name) +bool Cppyy::IsVariable(TCppScope_t scope) { -// Cut down to the outer-most scope from , taking proper care of templates. - int tpl_open = 0; - for (std::string::size_type pos = 0; pos < name.size(); ++pos) { - std::string::value_type c = name[pos]; - - // count '<' and '>' to be able to skip template contents - if (c == '<') - ++tpl_open; - else if (c == '>') - --tpl_open; - - // collect name up to "::" - else if (tpl_open == 0 && \ - c == ':' && pos+1 < name.size() && name[pos+1] == ':') { - // found the extend of the scope ... done - return name.substr(0, pos-1); - } - } - -// whole name is apparently a single scope - return name; + return CppDispatch::IsVariable(scope); } -static -std::string outer_no_template(const std::string& name) -{ -// Cut down to the outer-most scope from , drop templates - std::string::size_type first_scope = name.find(':'); - if (first_scope == std::string::npos) - return name.substr(0, name.find('<')); - std::string::size_type first_templ = name.find('<'); - if (first_templ == std::string::npos) - return name.substr(0, first_scope); - return name.substr(0, std::min(first_templ, first_scope)); -} - -#define FILL_COLL(type, filter) { \ - TIter itr{coll}; \ - type* obj = nullptr; \ - while ((obj = (type*)itr.Next())) { \ - const char* nm = obj->GetName(); \ - if (nm && nm[0] != '_' && !(obj->Property() & (filter))) { \ - if (gInitialNames.find(nm) == gInitialNames.end()) \ - cppnames.insert(nm); \ - }}} - -static inline -void cond_add(Cppyy::TCppScope_t scope, const std::string& ns_scope, - std::set& cppnames, const char* name, bool nofilter = false) -{ - if (!name || strstr(name, ".h") != 0) - return; - - if (scope == GLOBAL_HANDLE) { - std::string to_add = outer_no_template(name); - if ((nofilter || gInitialNames.find(to_add) == gInitialNames.end()) && !is_missclassified_stl(name)) - cppnames.insert(outer_no_template(name)); - } else if (scope == STD_HANDLE) { - if (strncmp(name, "std::", 5) == 0) { - name += 5; -#ifdef __APPLE__ - if (strncmp(name, "__1::", 5) == 0) name += 5; -#endif - } else if (!is_missclassified_stl(name)) - return; - cppnames.insert(outer_no_template(name)); - } else { - if (strncmp(name, ns_scope.c_str(), ns_scope.size()) == 0) - cppnames.insert(outer_with_template(name + ns_scope.size())); - } -} - -void Cppyy::GetAllCppNames(TCppScope_t scope, std::set& cppnames) +void Cppyy::GetAllCppNames(TCppScope_t scope, std::set& cppnames) { // Collect all known names of C++ entities under scope. This is useful for IDEs // employing tab-completion, for example. Note that functions names need not be // unique as they can be overloaded. - TClassRef& cr = type_from_handle(scope); - if (scope != GLOBAL_HANDLE && !(cr.GetClass() && cr->Property())) - return; - - std::string ns_scope = GetFinalName(scope); - if (scope != GLOBAL_HANDLE) ns_scope += "::"; - -// add existing values from read rootmap files if within this scope - TCollection* coll = gInterpreter->GetMapfile()->GetTable(); - { - TIter itr{coll}; - TEnvRec* ev = nullptr; - while ((ev = (TEnvRec*)itr.Next())) { - // TEnv contains rootmap entries and user-side rootmap files may be already - // loaded on startup. Thus, filter on file name rather than load time. - if (gRootSOs.find(ev->GetValue()) == gRootSOs.end()) - cond_add(scope, ns_scope, cppnames, ev->GetName(), true); - } - } - -// do we care about the class table or are the rootmap and list of types enough? -/* - gClassTable->Init(); - const int N = gClassTable->Classes(); - for (int i = 0; i < N; ++i) - cond_add(scope, ns_scope, cppnames, gClassTable->Next()); -*/ - -#if 0 -// add interpreted classes (no load) - { - ClassInfo_t* ci = gInterpreter->ClassInfo_FactoryWithScope( - false /* all */, scope == GLOBAL_HANDLE ? nullptr : cr->GetName()); - while (gInterpreter->ClassInfo_Next(ci)) { - const char* className = gInterpreter->ClassInfo_FullName(ci); - if (strstr(className, "(anonymous)") || strstr(className, "(unnamed)")) - continue; - cond_add(scope, ns_scope, cppnames, className); - } - gInterpreter->ClassInfo_Delete(ci); - } -#endif - -// any other types (e.g. that may have come from parsing headers) - coll = gROOT->GetListOfTypes(); - { - TIter itr{coll}; - TDataType* dt = nullptr; - while ((dt = (TDataType*)itr.Next())) { - if (!(dt->Property() & kIsFundamental)) { - cond_add(scope, ns_scope, cppnames, dt->GetName()); - } - } - } - -// add functions - coll = (scope == GLOBAL_HANDLE) ? - gROOT->GetListOfGlobalFunctions(true) : cr->GetListOfMethods(true); - { - TIter itr{coll}; - TFunction* obj = nullptr; - while ((obj = (TFunction*)itr.Next())) { - const char* nm = obj->GetName(); - // skip templated functions, adding only the un-instantiated ones - if (nm && gInitialNames.find(nm) == gInitialNames.end()) - cppnames.insert(nm); - } - } - -// add uninstantiated templates - coll = (scope == GLOBAL_HANDLE) ? - gROOT->GetListOfFunctionTemplates() : cr->GetListOfFunctionTemplates(true); - FILL_COLL(TFunctionTemplate, kIsPrivate | kIsProtected) - -// add (global) data members - if (scope == GLOBAL_HANDLE) { - coll = gROOT->GetListOfGlobals(); - FILL_COLL(TGlobal, kIsEnum | kIsPrivate | kIsProtected) - } else { - coll = cr->GetListOfDataMembers(); - FILL_COLL(TDataMember, kIsEnum | kIsPrivate | kIsProtected) - coll = cr->GetListOfUsingDataMembers(); - FILL_COLL(TDataMember, kIsEnum | kIsPrivate | kIsProtected) - } - -// add enums values only for user classes/namespaces - if (scope != GLOBAL_HANDLE && scope != STD_HANDLE) { - coll = cr->GetListOfEnums(); - FILL_COLL(TEnum, kIsPrivate | kIsProtected) - } - -#ifdef __APPLE__ -// special case for Apple, add version namespace '__1' entries to std - if (scope == STD_HANDLE) - GetAllCppNames(GetScope("std::__1"), cppnames); -#endif + CppDispatch::GetAllCppNames(scope, cppnames); } - - -// class reflection information ---------------------------------------------- +// // class reflection information ---------------------------------------------- std::vector Cppyy::GetUsingNamespaces(TCppScope_t scope) { - std::vector res; - if (!IsNamespace(scope)) - return res; - -#ifdef __APPLE__ - if (scope == STD_HANDLE) { - res.push_back(GetScope("__1")); - return res; - } -#endif - - TClassRef& cr = type_from_handle(scope); - if (!cr.GetClass() || !cr->GetClassInfo()) - return res; - - const std::vector& v = gInterpreter->GetUsingNamespaces(cr->GetClassInfo()); - res.reserve(v.size()); - for (const auto& uid : v) { - Cppyy::TCppScope_t uscope = GetScope(uid); - if (uscope) res.push_back(uscope); - } - - return res; + return CppDispatch::GetUsingNamespaces(scope); } - - -// class reflection information ---------------------------------------------- +// // class reflection information ---------------------------------------------- std::string Cppyy::GetFinalName(TCppType_t klass) { - if (klass == GLOBAL_HANDLE) - return ""; - TClassRef& cr = type_from_handle(klass); - std::string clName = cr->GetName(); -// TODO: why is this template splitting needed? - std::string::size_type pos = clName.substr(0, clName.find('<')).rfind("::"); - if (pos != std::string::npos) - return clName.substr(pos+2, std::string::npos); - return clName; + return CppDispatch::GetCompleteName(CppDispatch::GetUnderlyingScope(klass)); } std::string Cppyy::GetScopedFinalName(TCppType_t klass) { - if (klass == GLOBAL_HANDLE) - return ""; - TClassRef& cr = type_from_handle(klass); - if (cr.GetClass()) { - std::string name = cr->GetName(); - if (is_missclassified_stl(name)) - return std::string("std::")+cr->GetName(); - return cr->GetName(); - } - return ""; -} - -bool Cppyy::HasVirtualDestructor(TCppType_t klass) -{ - TClassRef& cr = type_from_handle(klass); - if (!cr.GetClass()) - return false; - - TFunction* f = cr->GetMethod(("~"+GetFinalName(klass)).c_str(), ""); - if (f && (f->Property() & kIsVirtual)) - return true; - - return false; + return CppDispatch::GetQualifiedCompleteName(klass); } -bool Cppyy::HasComplexHierarchy(TCppType_t klass) +bool Cppyy::HasVirtualDestructor(TCppScope_t scope) { - int is_complex = 1; - size_t nbases = 0; - - TClassRef& cr = type_from_handle(klass); - if (cr.GetClass() && cr->GetListOfBases() != 0) - nbases = GetNumBases(klass); - - if (1 < nbases) - is_complex = 1; - else if (nbases == 0) - is_complex = 0; - else { // one base class only - TBaseClass* base = (TBaseClass*)cr->GetListOfBases()->At(0); - if (base->Property() & kIsVirtualBase) - is_complex = 1; // TODO: verify; can be complex, need not be. - else - is_complex = HasComplexHierarchy(GetScope(base->GetName())); - } - - return is_complex; + TCppMethod_t func = CppDispatch::GetDestructor(scope); + return CppDispatch::IsVirtualMethod(func); } -Cppyy::TCppIndex_t Cppyy::GetNumBases(TCppType_t klass) +Cppyy::TCppIndex_t Cppyy::GetNumBases(TCppScope_t klass) { // Get the total number of base classes that this class has. - TClassRef& cr = type_from_handle(klass); - if (cr.GetClass() && cr->GetListOfBases() != 0) - return (TCppIndex_t)cr->GetListOfBases()->GetSize(); - return (TCppIndex_t)0; + return CppDispatch::GetNumBases(klass); } //////////////////////////////////////////////////////////////////////////////// -/// \fn Cppyy::TCppIndex_t GetLongestInheritancePath(TClass *klass) +/// \fn Cppyy::TCppIndex_t Cppyy::GetNumBasesLongestBranch(TCppScope_t klass) /// \brief Retrieve number of base classes in the longest branch of the /// inheritance tree of the input class. /// \param[in] klass The class to start the retrieval process from. @@ -1550,159 +1176,70 @@ Cppyy::TCppIndex_t Cppyy::GetNumBases(TCppType_t klass) /// /// calling this function on an instance of `C` will return 3, the steps /// required to go from C to X. -Cppyy::TCppIndex_t GetLongestInheritancePath(TClass *klass) -{ - - auto directbases = klass->GetListOfBases(); - if (!directbases) { - // This is a leaf with no bases - return 0; - } - auto ndirectbases = directbases->GetSize(); - if (ndirectbases == 0) { - // This is a leaf with no bases - return 0; - } else { - // If there is at least one direct base - std::vector nbases_branches; - nbases_branches.reserve(ndirectbases); - - // Traverse all direct bases of the current class and call the function - // recursively - for (auto baseclass : TRangeDynCast(directbases)) { - if (!baseclass) - continue; - if (auto baseclass_tclass = baseclass->GetClassPointer()) { - nbases_branches.emplace_back(GetLongestInheritancePath(baseclass_tclass)); - } - } - - // Get longest path among the direct bases of the current class - auto longestbranch = std::max_element(std::begin(nbases_branches), std::end(nbases_branches)); - - // Add 1 to include the current class in the count - return 1 + *longestbranch; - } +Cppyy::TCppIndex_t Cppyy::GetNumBasesLongestBranch(TCppScope_t klass) { + std::vector num; + for (TCppIndex_t ibase = 0; ibase < GetNumBases(klass); ++ibase) + num.push_back(GetNumBasesLongestBranch(Cppyy::GetBaseScope(klass, ibase))); + if (num.empty()) + return 0; + return *std::max_element(num.begin(), num.end()) + 1; } -//////////////////////////////////////////////////////////////////////////////// -/// \fn Cppyy::TCppIndex_t Cppyy::GetNumBasesLongest(TCppType_t klass) -/// \brief Retrieve number of base classes in the longest branch of the -/// inheritance tree. -/// \param[in] klass The class to start the retrieval process from. -/// -/// The function converts the input class to a `TClass *` and calls -/// GetLongestInheritancePath. -Cppyy::TCppIndex_t Cppyy::GetNumBasesLongestBranch(TCppType_t klass) +std::string Cppyy::GetBaseName(TCppType_t klass, TCppIndex_t ibase) { - - const auto &cr = type_from_handle(klass); - - if (auto klass_tclass = cr.GetClass()) { - return GetLongestInheritancePath(klass_tclass); - } - - // In any other case, return zero - return 0; + return CppDispatch::GetName(CppDispatch::GetBaseClass(klass, ibase)); } -std::string Cppyy::GetBaseName(TCppType_t klass, TCppIndex_t ibase) +Cppyy::TCppScope_t Cppyy::GetBaseScope(TCppScope_t klass, TCppIndex_t ibase) { - TClassRef& cr = type_from_handle(klass); - return ((TBaseClass*)cr->GetListOfBases()->At((int)ibase))->GetName(); + return CppDispatch::GetBaseClass(klass, ibase); } -bool Cppyy::IsSubtype(TCppType_t derived, TCppType_t base) +bool Cppyy::IsSubclass(TCppScope_t derived, TCppScope_t base) { - if (derived == base) - return true; - TClassRef& derived_type = type_from_handle(derived); - TClassRef& base_type = type_from_handle(base); - if (derived_type.GetClass() && base_type.GetClass()) - return derived_type->GetBaseClass(base_type) != 0; - return false; + return CppDispatch::IsSubclass(derived, base); } -bool Cppyy::IsSmartPtr(TCppType_t klass) +static std::set gSmartPtrTypes = + {"std::auto_ptr", "std::shared_ptr", "std::unique_ptr", "std::weak_ptr"}; + +bool Cppyy::IsSmartPtr(TCppScope_t klass) { - TClassRef& cr = type_from_handle(klass); - const std::string& tn = cr->GetName(); - if (gSmartPtrTypes.find(tn.substr(0, tn.find("<"))) != gSmartPtrTypes.end()) + const std::string& rn = Cppyy::GetScopedFinalName(klass); + if (gSmartPtrTypes.find(rn.substr(0, rn.find("<"))) != gSmartPtrTypes.end()) return true; return false; } bool Cppyy::GetSmartPtrInfo( - const std::string& tname, TCppType_t* raw, TCppMethod_t* deref) + const std::string& tname, TCppScope_t* raw, TCppMethod_t* deref) { + // TODO: We can directly accept scope instead of name const std::string& rn = ResolveName(tname); - if (gSmartPtrTypes.find(rn.substr(0, rn.find("<"))) != gSmartPtrTypes.end()) { - if (!raw && !deref) return true; - - TClassRef& cr = type_from_handle(GetScope(tname)); - if (cr.GetClass()) { - TFunction* func = cr->GetMethod("operator->", ""); - if (!func) - func = cr->GetMethod("operator->", ""); - if (func) { - if (deref) *deref = (TCppMethod_t)new_CallWrapper(func); - if (raw) *raw = GetScope(TClassEdit::ShortType( - func->GetReturnTypeNormalizedName().c_str(), 1)); - return (!deref || *deref) && (!raw || *raw); - } - } - } + if (gSmartPtrTypes.find(rn.substr(0, rn.find("<"))) == gSmartPtrTypes.end()) + return false; - return false; -} + if (!raw && !deref) return true; -void Cppyy::AddSmartPtrType(const std::string& type_name) -{ - gSmartPtrTypes.insert(ResolveName(type_name)); -} + TCppScope_t scope = Cppyy::GetScope(rn); + if (!scope) + return false; -void Cppyy::AddTypeReducer(const std::string& /*reducable*/, const std::string& /*reduced*/) -{ - // This function is deliberately left empty, because it is not used in - // PyROOT, and synchronizing it with cppyy-backend upstream would require - // patches to ROOT meta. -} + std::vector ops; + CppDispatch::GetOperator(scope, CppDispatch::Operator::OP_Arrow, ops, CppDispatch::OperatorArity::kBoth); + if (ops.size() != 1) + return false; + if (deref) *deref = ops[0]; + if (raw) *raw = Cppyy::GetScopeFromType(CppDispatch::GetFunctionReturnType(ops[0])); + return (!deref || *deref) && (!raw || *raw); +} // type offsets -------------------------------------------------------------- -ptrdiff_t Cppyy::GetBaseOffset(TCppType_t derived, TCppType_t base, +ptrdiff_t Cppyy::GetBaseOffset(TCppScope_t derived, TCppScope_t base, TCppObject_t address, int direction, bool rerror) { -// calculate offsets between declared and actual type, up-cast: direction > 0; down-cast: direction < 0 - if (derived == base || !(base && derived)) - return (ptrdiff_t)0; - - TClassRef& cd = type_from_handle(derived); - TClassRef& cb = type_from_handle(base); - - if (!cd.GetClass() || !cb.GetClass()) - return (ptrdiff_t)0; - - ptrdiff_t offset = -1; - if (!(cd->GetClassInfo() && cb->GetClassInfo())) { // gInterpreter requirement - // would like to warn, but can't quite determine error from intentional - // hiding by developers, so only cover the case where we really should have - // had a class info, but apparently don't: - if (cd->IsLoaded()) { - // warn to allow diagnostics - std::ostringstream msg; - msg << "failed offset calculation between " << cb->GetName() << " and " << cd->GetName(); - // TODO: propagate this warning to caller w/o use of Python C-API - // PyErr_Warn(PyExc_RuntimeWarning, const_cast(msg.str().c_str())); - std::cerr << "Warning: " << msg.str() << '\n'; - } - - // return -1 to signal caller NOT to apply offset - return rerror ? (ptrdiff_t)offset : 0; - } - - offset = gInterpreter->ClassInfo_GetBaseOffset( - cd->GetClassInfo(), cb->GetClassInfo(), (void*)address, direction > 0); + intptr_t offset = CppDispatch::GetBaseClassOffset(derived, base); if (offset == -1) // Cling error, treat silently return rerror ? (ptrdiff_t)offset : 0; @@ -1710,931 +1247,528 @@ ptrdiff_t Cppyy::GetBaseOffset(TCppType_t derived, TCppType_t base, } -// method/function reflection information ------------------------------------ -Cppyy::TCppIndex_t Cppyy::GetNumMethods(TCppScope_t scope, bool accept_namespace) +void Cppyy::GetClassMethods(TCppScope_t scope, std::vector &methods) { - if (!accept_namespace && IsNamespace(scope)) - return (TCppIndex_t)0; // enforce lazy - - if (scope == GLOBAL_HANDLE) - return gROOT->GetListOfGlobalFunctions(true)->GetSize(); - - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass() && cr->GetListOfMethods(true)) { - Cppyy::TCppIndex_t nMethods = (TCppIndex_t)cr->GetListOfMethods(false)->GetSize(); - if (nMethods == (TCppIndex_t)0) { - std::string clName = GetScopedFinalName(scope); - if (clName.find('<') != std::string::npos) { - // chicken-and-egg problem: TClass does not know about methods until - // instantiation, so force it - std::ostringstream stmt; - stmt << "template class " << clName << ";"; - gInterpreter->Declare(stmt.str().c_str()/*, silent = true*/); - - // now reload the methods - return (TCppIndex_t)cr->GetListOfMethods(true)->GetSize(); - } - } - return nMethods; - } - - return (TCppIndex_t)0; // unknown class? + CppDispatch::GetClassMethods(scope, methods); } -std::vector Cppyy::GetMethodIndicesFromName( +std::vector Cppyy::GetMethodsFromName( TCppScope_t scope, const std::string& name) { - std::vector indices; - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - gInterpreter->UpdateListOfMethods(cr.GetClass()); - int imeth = 0; - TFunction* func = nullptr; - TIter next(cr->GetListOfMethods()); - while ((func = (TFunction*)next())) { - if (match_name(name, func->GetName())) { - // C++ functions should be public to allow access; C functions have no access - // specifier and should always be accepted - auto prop = func->Property(); - if ((prop & kIsPublic) || !(prop & (kIsPrivate | kIsProtected | kIsPublic))) - indices.push_back((TCppIndex_t)imeth); - } - ++imeth; - } - } else if (scope == GLOBAL_HANDLE) { - TCollection* funcs = gROOT->GetListOfGlobalFunctions(true); - - // tickle deserialization - if (!funcs->FindObject(name.c_str())) - return indices; - - TFunction* func = nullptr; - TIter ifunc(funcs); - while ((func = (TFunction*)ifunc.Next())) { - if (match_name(name, func->GetName())) - indices.push_back((TCppIndex_t)new_CallWrapper(func)); - } - } - - return indices; + return CppDispatch::GetFunctionsUsingName(scope, name); } - -Cppyy::TCppMethod_t Cppyy::GetMethod(TCppScope_t scope, TCppIndex_t idx) -{ - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - TFunction* f = (TFunction*)cr->GetListOfMethods(false)->At((int)idx); - if (f) return (Cppyy::TCppMethod_t)new_CallWrapper(f); - return (Cppyy::TCppMethod_t)nullptr; - } - - assert(klass == (Cppyy::TCppType_t)GLOBAL_HANDLE); - return (Cppyy::TCppMethod_t)idx; -} - std::string Cppyy::GetMethodName(TCppMethod_t method) { - if (method) { - const std::string& name = ((CallWrapper*)method)->fName; - - if (name.compare(0, 8, "operator") != 0) - // strip template instantiation part, if any - return name.substr(0, name.find('<')); - return name; - } - return ""; + return CppDispatch::GetName(method); } std::string Cppyy::GetMethodFullName(TCppMethod_t method) { - if (method) { - std::string name = ((CallWrapper*)method)->fName; - name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); - return name; - } - return ""; + return CppDispatch::GetCompleteName(method); } -std::string Cppyy::GetMethodMangledName(TCppMethod_t method) +Cppyy::TCppType_t Cppyy::GetMethodReturnType(TCppMethod_t method) { - if (method) - return m2f(method)->GetMangledName(); - return ""; + return CppDispatch::GetFunctionReturnType(method); } -std::string Cppyy::GetMethodResultType(TCppMethod_t method) +std::string Cppyy::GetMethodReturnTypeAsString(TCppMethod_t method) { - if (method) { - TFunction* f = m2f(method); - if (f->ExtraProperty() & kIsConstructor) - return "constructor"; - std::string restype = f->GetReturnTypeName(); - // TODO: this is ugly; GetReturnTypeName() keeps typedefs, but may miss scopes - // for some reason; GetReturnTypeNormalizedName() has been modified to return - // the canonical type to guarantee correct namespaces. Sometimes typedefs look - // better, sometimes not, sometimes it's debatable (e.g. vector::size_type). - // So, for correctness sake, GetReturnTypeNormalizedName() is used, except for a - // special case of uint8_t/int8_t that must propagate as their typedefs. - if (restype.find("int8_t") != std::string::npos) - return restype; - restype = f->GetReturnTypeNormalizedName(); - if (restype == "(lambda)") { - std::ostringstream s; - // TODO: what if there are parameters to the lambda? - s << "__cling_internal::FT::F"; - TClass* cl = TClass::GetClass(s.str().c_str()); - if (cl) return cl->GetName(); - // TODO: signal some type of error (or should that be upstream? - } - return restype; - } - return ""; + return + CppDispatch::GetTypeAsString( + CppDispatch::GetCanonicalType( + CppDispatch::GetFunctionReturnType(method))); } Cppyy::TCppIndex_t Cppyy::GetMethodNumArgs(TCppMethod_t method) { - if (method) - return m2f(method)->GetNargs(); - return 0; + return CppDispatch::GetFunctionNumArgs(method); } Cppyy::TCppIndex_t Cppyy::GetMethodReqArgs(TCppMethod_t method) { - if (method) { - TFunction* f = m2f(method); - return (TCppIndex_t)(f->GetNargs() - f->GetNargsOpt()); - } - return (TCppIndex_t)0; + return CppDispatch::GetFunctionRequiredArgs(method); } std::string Cppyy::GetMethodArgName(TCppMethod_t method, TCppIndex_t iarg) { - if (method) { - TFunction* f = m2f(method); - TMethodArg* arg = (TMethodArg*)f->GetListOfMethodArgs()->At((int)iarg); - return arg->GetName(); - } - return ""; + if (!method) + return ""; + + return CppDispatch::GetFunctionArgName(method, iarg); } -std::string Cppyy::GetMethodArgType(TCppMethod_t method, TCppIndex_t iarg) +Cppyy::TCppType_t Cppyy::GetMethodArgType(TCppMethod_t method, TCppIndex_t iarg) { - if (method) { - TFunction* f = m2f(method); - TMethodArg* arg = (TMethodArg*)f->GetListOfMethodArgs()->At((int)iarg); - std::string ft = arg->GetFullTypeName(); - if (ft.rfind("enum ", 0) != std::string::npos) { // special case to preserve 'enum' tag - std::string arg_type = arg->GetTypeNormalizedName(); - return arg_type.insert(arg_type.rfind("const ", 0) == std::string::npos ? 0 : 6, "enum "); - } else if (g_builtins.find(ft) != g_builtins.end() || ft.find("int8_t") != std::string::npos) - return ft; // do not resolve int8_t and uint8_t typedefs + return CppDispatch::GetFunctionArgType(method, iarg); +} - return arg->GetTypeNormalizedName(); - } - return ""; +std::string Cppyy::GetMethodArgTypeAsString(TCppMethod_t method, TCppIndex_t iarg) +{ + return CppDispatch::GetTypeAsString( + CppDispatch::GetFunctionArgType(method, iarg)); } -Cppyy::TCppIndex_t Cppyy::CompareMethodArgType(TCppMethod_t method, TCppIndex_t iarg, const std::string &req_type) +std::string Cppyy::GetMethodArgCanonTypeAsString(TCppMethod_t method, TCppIndex_t iarg) { - if (method) { - TFunction* f = m2f(method); - TMethodArg* arg = (TMethodArg *)f->GetListOfMethodArgs()->At((int)iarg); - void *argqtp = gInterpreter->TypeInfo_QualTypePtr(arg->GetTypeInfo()); - - TypeInfo_t *reqti = gInterpreter->TypeInfo_Factory(req_type.c_str()); - void *reqqtp = gInterpreter->TypeInfo_QualTypePtr(reqti); - - // This scoring is not based on any particular rules - if (gInterpreter->IsSameType(argqtp, reqqtp)) - return 0; // Best match - else if ((gInterpreter->IsSignedIntegerType(argqtp) && gInterpreter->IsSignedIntegerType(reqqtp)) || - (gInterpreter->IsUnsignedIntegerType(argqtp) && gInterpreter->IsUnsignedIntegerType(reqqtp)) || - (gInterpreter->IsFloatingType(argqtp) && gInterpreter->IsFloatingType(reqqtp))) - return 1; - else if ((gInterpreter->IsSignedIntegerType(argqtp) && gInterpreter->IsUnsignedIntegerType(reqqtp)) || - (gInterpreter->IsFloatingType(argqtp) && gInterpreter->IsUnsignedIntegerType(reqqtp))) - return 2; - else if ((gInterpreter->IsIntegerType(argqtp) && gInterpreter->IsIntegerType(reqqtp))) - return 3; - else if ((gInterpreter->IsVoidPointerType(argqtp) && gInterpreter->IsPointerType(reqqtp))) - return 4; - else - return 10; // Penalize heavily for no possible match - } - return INT_MAX; // Method is not valid + return + CppDispatch::GetTypeAsString( + CppDispatch::GetCanonicalType( + CppDispatch::GetFunctionArgType(method, iarg))); } std::string Cppyy::GetMethodArgDefault(TCppMethod_t method, TCppIndex_t iarg) { - if (method) { - TFunction* f = m2f(method); - TMethodArg* arg = (TMethodArg*)f->GetListOfMethodArgs()->At((int)iarg); - const char* def = arg->GetDefault(); - if (def) - return def; - } - - return ""; + if (!method) + return ""; + return CppDispatch::GetFunctionArgDefault(method, iarg); } -std::string Cppyy::GetMethodSignature(TCppMethod_t method, bool show_formalargs, TCppIndex_t maxargs) -{ - TFunction* f = m2f(method); - if (f) { - std::ostringstream sig; - sig << "("; - int nArgs = f->GetNargs(); - if (maxargs != (TCppIndex_t)-1) nArgs = std::min(nArgs, (int)maxargs); - for (int iarg = 0; iarg < nArgs; ++iarg) { - TMethodArg* arg = (TMethodArg*)f->GetListOfMethodArgs()->At(iarg); - sig << arg->GetFullTypeName(); - if (show_formalargs) { - const char* argname = arg->GetName(); - if (argname && argname[0] != '\0') sig << " " << argname; - const char* defvalue = arg->GetDefault(); - if (defvalue && defvalue[0] != '\0') sig << " = " << defvalue; - } - if (iarg != nArgs-1) sig << (show_formalargs ? ", " : ","); +Cppyy::TCppIndex_t Cppyy::CompareMethodArgType(TCppMethod_t method, TCppIndex_t iarg, const std::string &req_type) +{ + // if (method) { + // TFunction* f = m2f(method); + // TMethodArg* arg = (TMethodArg *)f->GetListOfMethodArgs()->At((int)iarg); + // void *argqtp = gInterpreter->TypeInfo_QualTypePtr(arg->GetTypeInfo()); + + // TypeInfo_t *reqti = gInterpreter->TypeInfo_Factory(req_type.c_str()); + // void *reqqtp = gInterpreter->TypeInfo_QualTypePtr(reqti); + + // if (ArgSimilarityScore(argqtp, reqqtp) < 10) { + // return ArgSimilarityScore(argqtp, reqqtp); + // } + // else { // Match using underlying types + // if(gInterpreter->IsPointerType(argqtp)) + // argqtp = gInterpreter->TypeInfo_QualTypePtr(gInterpreter->GetPointerType(argqtp)); + + // // Handles reference types and strips qualifiers + // TypeInfo_t *arg_ul = gInterpreter->GetNonReferenceType(argqtp); + // TypeInfo_t *req_ul = gInterpreter->GetNonReferenceType(reqqtp); + // argqtp = gInterpreter->TypeInfo_QualTypePtr(gInterpreter->GetUnqualifiedType(gInterpreter->TypeInfo_QualTypePtr(arg_ul))); + // reqqtp = gInterpreter->TypeInfo_QualTypePtr(gInterpreter->GetUnqualifiedType(gInterpreter->TypeInfo_QualTypePtr(req_ul))); + + // return ArgSimilarityScore(argqtp, reqqtp); + // } + // } + return 0; // Method is not valid +} + +std::string Cppyy::GetMethodSignature(TCppMethod_t method, bool show_formal_args, TCppIndex_t max_args) +{ + std::ostringstream sig; + sig << "("; + int nArgs = GetMethodNumArgs(method); + if (max_args != (TCppIndex_t)-1) nArgs = std::min(nArgs, (int)max_args); + for (int iarg = 0; iarg < nArgs; ++iarg) { + sig << Cppyy::GetMethodArgTypeAsString(method, iarg); + if (show_formal_args) { + std::string argname = Cppyy::GetMethodArgName(method, iarg); + if (!argname.empty()) sig << " " << argname; + std::string defvalue = Cppyy::GetMethodArgDefault(method, iarg); + if (!defvalue.empty()) sig << " = " << defvalue; } - sig << ")"; - return sig.str(); + if (iarg != nArgs-1) sig << ", "; } - return ""; + sig << ")"; + return sig.str(); } -std::string Cppyy::GetMethodPrototype(TCppScope_t scope, TCppMethod_t method, bool show_formalargs) +std::string Cppyy::GetMethodPrototype(TCppMethod_t method, bool show_formal_args) { - std::string scName = GetScopedFinalName(scope); - TFunction* f = m2f(method); - if (f) { - std::ostringstream sig; - sig << f->GetReturnTypeName() << " " - << scName << "::" << f->GetName(); - sig << GetMethodSignature(method, show_formalargs); - return sig.str(); - } - return ""; + assert(0 && "Unused"); + return ""; // return CppDispatch::GetFunctionPrototype(method, show_formal_args); } bool Cppyy::IsConstMethod(TCppMethod_t method) { - if (method) { - TFunction* f = m2f(method); - return f->Property() & kIsConstMethod; - } - return false; + if (!method) + return false; + return CppDispatch::IsConstMethod(method); } -Cppyy::TCppIndex_t Cppyy::GetNumTemplatedMethods(TCppScope_t scope, bool accept_namespace) +void Cppyy::GetTemplatedMethods(TCppScope_t scope, std::vector &methods) { - if (!accept_namespace && IsNamespace(scope)) - return (TCppIndex_t)0; // enforce lazy - - if (scope == GLOBAL_HANDLE) { - TCollection* coll = gROOT->GetListOfFunctionTemplates(); - if (coll) return (TCppIndex_t)coll->GetSize(); - } else { - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - TCollection* coll = cr->GetListOfFunctionTemplates(true); - if (coll) return (TCppIndex_t)coll->GetSize(); - } - } - -// failure ... - return (TCppIndex_t)0; + CppDispatch::GetFunctionTemplatedDecls(scope, methods); } -std::string Cppyy::GetTemplatedMethodName(TCppScope_t scope, TCppIndex_t imeth) +Cppyy::TCppIndex_t Cppyy::GetNumTemplatedMethods(TCppScope_t scope, bool accept_namespace) { - if (scope == (TCppScope_t)GLOBAL_HANDLE) - return ((THashList*)gROOT->GetListOfFunctionTemplates())->At((int)imeth)->GetName(); - else { - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) - return cr->GetListOfFunctionTemplates(false)->At((int)imeth)->GetName(); - } - -// failure ... - assert(!"should not be called unless GetNumTemplatedMethods() succeeded"); - return ""; + std::vector mc; + CppDispatch::GetFunctionTemplatedDecls(scope, mc); + return mc.size(); } -bool Cppyy::IsTemplatedConstructor(TCppScope_t scope, TCppIndex_t imeth) +std::string Cppyy::GetTemplatedMethodName(TCppScope_t scope, TCppIndex_t imeth) { - if (scope == (TCppScope_t)GLOBAL_HANDLE) - return false; + std::vector mc; + CppDispatch::GetFunctionTemplatedDecls(scope, mc); - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - TFunctionTemplate* f = (TFunctionTemplate*)cr->GetListOfFunctionTemplates(false)->At((int)imeth); - return f->ExtraProperty() & kIsConstructor; - } + if (imeth < mc.size()) return GetMethodName(mc[imeth]); - return false; + return ""; } bool Cppyy::ExistsMethodTemplate(TCppScope_t scope, const std::string& name) { - if (scope == (TCppScope_t)GLOBAL_HANDLE) - return (bool)gROOT->GetFunctionTemplate(name.c_str()); - else { - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) - return (bool)cr->GetFunctionTemplate(name.c_str()); - } - -// failure ... - return false; + return CppDispatch::ExistsFunctionTemplate(name, scope); } -bool Cppyy::IsStaticTemplate(TCppScope_t scope, const std::string& name) +bool Cppyy::IsTemplatedMethod(TCppMethod_t method) { - TFunctionTemplate* tf = nullptr; - if (scope == (TCppScope_t)GLOBAL_HANDLE) - tf = gROOT->GetFunctionTemplate(name.c_str()); - else { - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) - tf = cr->GetFunctionTemplate(name.c_str()); - } - - if (!tf) return false; - - return (bool)(tf->Property() & kIsStatic); + return CppDispatch::IsTemplatedFunction(method); } -bool Cppyy::IsMethodTemplate(TCppScope_t scope, TCppIndex_t idx) +bool Cppyy::IsStaticTemplate(TCppScope_t scope, const std::string& name) { - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - TFunction* f = (TFunction*)cr->GetListOfMethods(false)->At((int)idx); - if (f && strstr(f->GetName(), "<")) return true; - return false; - } - - assert(scope == (Cppyy::TCppType_t)GLOBAL_HANDLE); - if (((CallWrapper*)idx)->fName.find('<') != std::string::npos) return true; + if (CppDispatch::TCppFunction_t tf = GetMethodTemplate(scope, name, "")) + return CppDispatch::IsStaticMethod(tf); return false; } -// helpers for Cppyy::GetMethodTemplate() -static std::map gMethodTemplates; - -static inline -void remove_space(std::string& n) { - std::string::iterator pos = std::remove_if(n.begin(), n.end(), isspace); - n.erase(pos, n.end()); -} - -static inline -bool template_compare(std::string n1, std::string n2) { - if (n1.back() == '>') n1 = n1.substr(0, n1.size()-1); - remove_space(n1); - remove_space(n2); - return n2.compare(0, n1.size(), n1) == 0; -} - Cppyy::TCppMethod_t Cppyy::GetMethodTemplate( TCppScope_t scope, const std::string& name, const std::string& proto) { -// There is currently no clean way of extracting a templated method out of ROOT/meta -// for a variety of reasons, none of them fundamental. The game played below is to -// first get any pre-existing functions already managed by ROOT/meta, but if that fails, -// to do an explicit lookup that ignores the prototype (i.e. the full name should be -// enough), and finally to ignore the template arguments part of the name as this fails -// in cling if there are default parameters. - TFunction* func = nullptr; ClassInfo_t* cl = nullptr; - if (scope == (TCppScope_t)GLOBAL_HANDLE) { - func = gROOT->GetGlobalFunctionWithPrototype(name.c_str(), proto.c_str()); - if (func && name.back() == '>') { - // make sure that all template parameters match (more are okay, e.g. defaults or - // ones derived from the arguments or variadic templates) - if (!template_compare(name, func->GetName())) - func = nullptr; // happens if implicit conversion matches the overload - } + std::string pureName; + std::string explicit_params; + + if ((name.find("operator<") != 0) && + (name.find('<') != std::string::npos)) { + pureName = name.substr(0, name.find('<')); + size_t start = name.find('<'); + size_t end = name.rfind('>'); + explicit_params = name.substr(start + 1, end - start - 1); } else { - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - func = cr->GetMethodWithPrototype(name.c_str(), proto.c_str()); - if (!func) { - cl = cr->GetClassInfo(); - // try base classes to cover a common 'using' case (TODO: this is stupid and misses - // out on base classes; fix that with improved access to Cling) - TCppIndex_t nbases = GetNumBases(scope); - for (TCppIndex_t i = 0; i < nbases; ++i) { - TClassRef& base = type_from_handle(GetScope(GetBaseName(scope, i))); - if (base.GetClass()) { - func = base->GetMethodWithPrototype(name.c_str(), proto.c_str()); - if (func) break; - } - } - } - } + pureName = name; } - if (!func && name.back() == '>' && (cl || scope == (TCppScope_t)GLOBAL_HANDLE)) { - // try again, ignoring proto in case full name is complete template - auto declid = gInterpreter->GetFunction(cl, name.c_str()); - if (declid) { - auto existing = gMethodTemplates.find(declid); - if (existing == gMethodTemplates.end()) { - auto cw = new_CallWrapper(declid, name); - existing = gMethodTemplates.insert(std::make_pair(declid, cw)).first; - } - return (TCppMethod_t)existing->second; - } + std::vector unresolved_candidate_methods; + CppDispatch::GetClassTemplatedMethods(pureName, scope, + unresolved_candidate_methods); + if (unresolved_candidate_methods.empty() && name.find("operator") == 0) { + // try operators + Cppyy::GetClassOperators(scope, pureName, unresolved_candidate_methods); } - if (func) { - // make sure we didn't match a non-templated overload - if (func->ExtraProperty() & kIsTemplateSpec) - return (TCppMethod_t)new_CallWrapper(func); + // CPyCppyy assumes that we attempt instantiation here + std::vector arg_types; + std::vector templ_params; + Cppyy::AppendTypesSlow(proto, arg_types, scope); + Cppyy::AppendTypesSlow(explicit_params, templ_params, scope); - // disregard this non-templated method as it will be considered when appropriate - return (TCppMethod_t)nullptr; - } + Cppyy::TCppMethod_t cppmeth = CppDispatch::BestOverloadFunctionMatch( + unresolved_candidate_methods, templ_params, arg_types); -// try again with template arguments removed from name, if applicable - if (name.back() == '>') { - auto pos = name.find('<'); - if (pos != std::string::npos) { - TCppMethod_t cppmeth = GetMethodTemplate(scope, name.substr(0, pos), proto); - if (cppmeth) { - // allow if requested template names match up to the result - const std::string& alt = GetMethodFullName(cppmeth); - if (name.size() < alt.size() && alt.find('<') == pos) { - if (template_compare(name, alt)) - return cppmeth; - } - } - } - } + if (!cppmeth && unresolved_candidate_methods.size() == 1 && + !templ_params.empty()) + cppmeth = + CppDispatch::InstantiateTemplate(unresolved_candidate_methods[0], + templ_params.data(), templ_params.size(), false); + + return cppmeth; + + // if it fails, use Sema to propogate info about why it failed (DeductionInfo) -// failure ... - return (TCppMethod_t)nullptr; } -static inline -std::string type_remap(const std::string& n1, const std::string& n2) -{ -// Operator lookups of (C++ string, Python str) should succeed for the combos of -// string/str, wstring/str, string/unicode and wstring/unicode; since C++ does not have a -// operator+(std::string, std::wstring), we'll have to look up the same type and rely on -// the converters in CPyCppyy/_cppyy. - if (n1 == "str" || n1 == "unicode") { - if (n2 == "std::basic_string,std::allocator >") - return n2; // match like for like - return "std::string"; // probably best bet +static inline std::string type_remap(const std::string& n1, + const std::string& n2) { + // Operator lookups of (C++ string, Python str) should succeed for the + // combos of string/str, wstring/str, string/unicode and wstring/unicode; + // since C++ does not have a operator+(std::string, std::wstring), we'll + // have to look up the same type and rely on the converters in + // CPyCppyy/_cppyy. + if (n1 == "str" || n1 == "unicode" || n1 == "std::basic_string") { + if (n2 == "std::basic_string") + return "std::basic_string&"; // match like for like + return "std::basic_string&"; // probably best bet + } else if (n1 == "std::basic_string") { + return "std::basic_string&"; } else if (n1 == "float") { - return "double"; // debatable, but probably intended + return "double"; // debatable, but probably intended } else if (n1 == "complex") { return "std::complex"; } return n1; } -Cppyy::TCppIndex_t Cppyy::GetGlobalOperator( +void Cppyy::GetClassOperators(Cppyy::TCppScope_t klass, + const std::string& opname, + std::vector& operators) { + std::string op = opname.substr(8); + CppDispatch::GetOperator(klass, CppDispatch::GetOperatorFromSpelling(op), operators, CppDispatch::OperatorArity::kBoth); +} + +Cppyy::TCppMethod_t Cppyy::GetGlobalOperator( TCppType_t scope, const std::string& lc, const std::string& rc, const std::string& opname) { -// Find a global operator function with a matching signature; prefer by-ref, but -// fall back on by-value if that fails. - std::string lcname1 = TClassEdit::CleanType(lc.c_str()); - const std::string& rcname = rc.empty() ? rc : type_remap(TClassEdit::CleanType(rc.c_str()), lcname1); - const std::string& lcname = type_remap(lcname1, rcname); - - std::string proto = lcname + "&" + (rc.empty() ? rc : (", " + rcname + "&")); - if (scope == (TCppScope_t)GLOBAL_HANDLE) { - TFunction* func = gROOT->GetGlobalFunctionWithPrototype(opname.c_str(), proto.c_str()); - if (func) return (TCppIndex_t)new_CallWrapper(func); - proto = lcname + (rc.empty() ? rc : (", " + rcname)); - func = gROOT->GetGlobalFunctionWithPrototype(opname.c_str(), proto.c_str()); - if (func) return (TCppIndex_t)new_CallWrapper(func); - } else { - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - TFunction* func = cr->GetMethodWithPrototype(opname.c_str(), proto.c_str()); - if (func) return (TCppIndex_t)cr->GetListOfMethods()->IndexOf(func); - proto = lcname + (rc.empty() ? rc : (", " + rcname)); - func = cr->GetMethodWithPrototype(opname.c_str(), proto.c_str()); - if (func) return (TCppIndex_t)cr->GetListOfMethods()->IndexOf(func); - } + std::string rc_type = type_remap(rc, lc); + std::string lc_type = type_remap(lc, rc); + bool is_templated = false; + if ((lc_type.find('<') != std::string::npos) || + (rc_type.find('<') != std::string::npos)) { + is_templated = true; } -// failure ... - return (TCppIndex_t)-1; -} + std::vector overloads; + CppDispatch::GetOperator(scope, CppDispatch::GetOperatorFromSpelling(opname), overloads, CppDispatch::OperatorArity::kBoth); -// method properties --------------------------------------------------------- + std::vector unresolved_candidate_methods; + for (auto overload: overloads) { + if (CppDispatch::IsTemplatedFunction(overload)) { + unresolved_candidate_methods.push_back(overload); + continue; + } else { + TCppType_t lhs_type = CppDispatch::GetFunctionArgType(overload, 0); + if (lc_type != + CppDispatch::GetTypeAsString(CppDispatch::GetUnderlyingType(lhs_type))) + continue; -static inline bool testMethodProperty(Cppyy::TCppMethod_t method, EProperty prop) -{ - if (!method) - return false; - TFunction *f = m2f(method); - return f->Property() & prop; + if (!rc_type.empty()) { + if (CppDispatch::GetFunctionNumArgs(overload) != 2) + continue; + TCppType_t rhs_type = CppDispatch::GetFunctionArgType(overload, 1); + if (rc_type != + CppDispatch::GetTypeAsString(CppDispatch::GetUnderlyingType(rhs_type))) + continue; + } + return overload; + } + } + if (is_templated) { + std::string lc_template = lc_type.substr( + lc_type.find("<") + 1, lc_type.rfind(">") - lc_type.find("<") - 1); + std::string rc_template = rc_type.substr( + rc_type.find("<") + 1, rc_type.rfind(">") - rc_type.find("<") - 1); + + std::vector arg_types; + if (auto l = Cppyy::GetType(lc_type, true)) + arg_types.emplace_back(l); + else + return nullptr; + + if (!rc_type.empty()) { + if (auto r = Cppyy::GetType(rc_type, true)) + arg_types.emplace_back(r); + else + return nullptr; + } + Cppyy::TCppMethod_t cppmeth = CppDispatch::BestOverloadFunctionMatch( + unresolved_candidate_methods, {}, arg_types); + if (cppmeth) + return cppmeth; + } + { + // we are trying to do a madeup IntegralToFloating implicit cast emulating clang + bool flag = false; + if (rc_type == "int") { + rc_type = "double"; + flag = true; + } + if (lc_type == "int") { + lc_type = "double"; + flag = true; + } + if (flag) + return GetGlobalOperator(scope, lc_type, rc_type, opname); + } + return nullptr; } -static inline bool testMethodExtraProperty(Cppyy::TCppMethod_t method, EFunctionProperty prop) +// // method properties --------------------------------------------------------- +bool Cppyy::IsDeletedMethod(TCppMethod_t method) { - if (!method) - return false; - TFunction *f = m2f(method); - return f->ExtraProperty() & prop; + return CppDispatch::IsFunctionDeleted(method); } bool Cppyy::IsPublicMethod(TCppMethod_t method) { - return testMethodProperty(method, kIsPublic); + return CppDispatch::IsPublicMethod(method); } bool Cppyy::IsProtectedMethod(TCppMethod_t method) { - return testMethodProperty(method, kIsProtected); + return CppDispatch::IsProtectedMethod(method); +} + +bool Cppyy::IsPrivateMethod(TCppMethod_t method) +{ + return CppDispatch::IsPrivateMethod(method); } bool Cppyy::IsConstructor(TCppMethod_t method) { - return testMethodExtraProperty(method, kIsConstructor); + return CppDispatch::IsConstructor(method); } bool Cppyy::IsDestructor(TCppMethod_t method) { - return testMethodExtraProperty(method, kIsDestructor); + return CppDispatch::IsDestructor(method); } bool Cppyy::IsStaticMethod(TCppMethod_t method) { - return testMethodProperty(method, kIsStatic); + return CppDispatch::IsStaticMethod(method); } -bool Cppyy::IsExplicit(TCppMethod_t method) +void Cppyy::GetDatamembers(TCppScope_t scope, std::vector& datamembers) { - return testMethodProperty(method, kIsExplicit); + CppDispatch::GetDatamembers(scope, datamembers); + CppDispatch::GetStaticDatamembers(scope, datamembers); + CppDispatch::GetEnumConstantDatamembers(scope, datamembers, false); } -// data member reflection information ---------------------------------------- -Cppyy::TCppIndex_t Cppyy::GetNumDatamembers(TCppScope_t scope, bool accept_namespace) -{ - if (!accept_namespace && IsNamespace(scope)) - return (TCppIndex_t)0; // enforce lazy - - if (scope == GLOBAL_HANDLE) - return gROOT->GetListOfGlobals(true)->GetSize(); - - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass() && cr->GetListOfDataMembers()) - return cr->GetListOfDataMembers()->GetSize(); - - return (TCppIndex_t)0; // unknown class? +bool Cppyy::CheckDatamember(TCppScope_t scope, const std::string& name) { + return (bool) CppDispatch::LookupDatamember(name, scope); } -std::string Cppyy::GetDatamemberName(TCppScope_t scope, TCppIndex_t idata) -{ - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At((int)idata); - return m->GetName(); - } - assert(scope == GLOBAL_HANDLE); - TGlobal* gbl = g_globalvars[idata]; - return gbl->GetName(); +bool Cppyy::IsLambdaClass(TCppType_t type) { + return CppDispatch::IsLambdaClass(type); } -static inline -int count_scopes(const std::string& tpname) -{ - int count = 0; - std::string::size_type pos = tpname.find("::", 0); - while (pos != std::string::npos) { - count++; - pos = tpname.find("::", pos+1); +Cppyy::TCppScope_t Cppyy::WrapLambdaFromVariable(TCppScope_t var) { + std::ostringstream code; + std::string name = Cppyy::GetFinalName(var); + code << "namespace __cppyy_internal_wrap_g {\n" + << " " << "std::function " << name << " = ::" << CppDispatch::GetQualifiedName(var) << ";\n" + << "}\n"; + + std::cout<< "\nCODE: " << code.str() << "\n"; + if (Cppyy::Compile(code.str().c_str())) { + TCppScope_t res = CppDispatch::GetNamed(name, CppDispatch::GetScope("__cppyy_internal_wrap_g", nullptr)); + if (res) return res; } - return count; + return var; } -std::string Cppyy::GetDatamemberType(TCppScope_t scope, TCppIndex_t idata) -{ - if (scope == GLOBAL_HANDLE) { - TGlobal* gbl = g_globalvars[idata]; - std::string fullType = gbl->GetFullTypeName(); +Cppyy::TCppScope_t Cppyy::AdaptFunctionForLambdaReturn(TCppScope_t fn) { + std::string fn_name = CppDispatch::GetQualifiedCompleteName(fn); + std::string signature = Cppyy::GetMethodSignature(fn, true); - if ((int)gbl->GetArrayDim()) { - std::ostringstream s; - for (int i = 0; i < (int)gbl->GetArrayDim(); ++i) - s << '[' << gbl->GetMaxIndex(i) << ']'; - fullType.append(s.str()); - } - return fullType; + std::ostringstream call; + call << "("; + for (size_t i = 0, n = Cppyy::GetMethodNumArgs(fn); i < n; i++) { + call << Cppyy::GetMethodArgName(fn, i); + if (i != n - 1) + call << ", "; } - - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At((int)idata); - // TODO: fix this upstream ... Usually, we want m->GetFullTypeName(), because it - // does not resolve typedefs, but it looses scopes for inner classes/structs, it - // doesn't resolve constexpr (leaving unresolved names), leaves spurious "struct" - // or "union" in the name, and can not handle anonymous unions. In that case - // m->GetTrueTypeName() should be used. W/o clear criteria to determine all these - // cases, the general rules are to prefer the true name if the full type does not - // exist as a type for classes, and the most scoped name otherwise. - const char* ft = m->GetFullTypeName(); std::string fullType = ft ? ft : ""; - const char* tn = m->GetTrueTypeName(); std::string trueName = tn ? tn : ""; - if (!trueName.empty() && fullType != trueName && !IsBuiltin(trueName)) { - if ( (!TClass::GetClass(fullType.c_str()) && TClass::GetClass(trueName.c_str())) || \ - (count_scopes(trueName) > count_scopes(fullType)) ) { - bool is_enum_tag = fullType.rfind("enum ", 0) != std::string::npos; - fullType = trueName; - if (is_enum_tag) - fullType.insert(fullType.rfind("const ", 0) == std::string::npos ? 0 : 6, "enum "); - } - } - - if ((int)m->GetArrayDim()) { - std::ostringstream s; - for (int i = 0; i < (int)m->GetArrayDim(); ++i) - s << '[' << m->GetMaxIndex(i) << ']'; - fullType.append(s.str()); - } - -#if 0 - // this is the only place where anonymous structs are uniquely identified, so setup - // a class if needed, such that subsequent GetScope() and GetScopedFinalName() calls - // return the uniquely named class - auto declid = m->GetTagDeclId(); //GetDeclId(); - if (declid && (m->Property() & (kIsClass | kIsStruct | kIsUnion)) &&\ - (fullType.find("(anonymous)") != std::string::npos || fullType.find("(unnamed)") != std::string::npos)) { - - // use the (fixed) decl id address to guarantee a unique name, even when there - // are multiple anonymous structs in the parent scope - std::ostringstream fulls; - fulls << fullType << "@" << (void*)declid; - fullType = fulls.str(); - - if (g_name2classrefidx.find(fullType) == g_name2classrefidx.end()) { - ClassInfo_t* ci = gInterpreter->ClassInfo_Factory(declid); - TClass* cl = gInterpreter->GenerateTClass(ci, kTRUE /* silent */); - gInterpreter->ClassInfo_Delete(ci); - if (cl) cl->SetName(fullType.c_str()); - g_name2classrefidx[fullType] = g_classrefs.size(); - g_classrefs.emplace_back(cl); - } - } -#endif - return fullType; + call << ")"; + + std::ostringstream code; + static int i = 0; + std::string name = "lambda_return_convert_" + std::to_string(++i); + code << "namespace __cppyy_internal_wrap_g {\n" + << "auto " << name << signature << "{" << "return std::function(" << fn_name << call.str() << "); }\n" + << "}\n"; + std::cout<< "\nCODE: " << code.str() << "\n"; + if (Cppyy::Compile(code.str().c_str())) { + TCppScope_t res = CppDispatch::GetNamed(name, CppDispatch::GetScope("__cppyy_internal_wrap_g", nullptr)); + if (res) return res; } - - return ""; + return fn; } -intptr_t Cppyy::GetDatamemberOffset(TCppScope_t scope, TCppIndex_t idata) +Cppyy::TCppType_t Cppyy::GetDatamemberType(TCppScope_t var) { - if (scope == GLOBAL_HANDLE) { - TGlobal* gbl = g_globalvars[idata]; - if (!gbl->GetAddress() || gbl->GetAddress() == (void*)-1) { - // CLING WORKAROUND: make sure variable is loaded - intptr_t addr = (intptr_t)gInterpreter->ProcessLine((std::string("&")+gbl->GetName()+";").c_str()); - if (gbl->GetAddress() && gbl->GetAddress() != (void*)-1) - return (intptr_t)gbl->GetAddress(); // now loaded! - return addr; // last resort ... - } - return (intptr_t)gbl->GetAddress(); - } - - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At((int)idata); - // CLING WORKAROUND: the following causes templates to be instantiated first within the proper - // scope, making the lookup succeed and preventing spurious duplicate instantiations later. Also, - // if the variable is not yet loaded, pull it in through gInterpreter. - intptr_t offset = (intptr_t)-1; - if (m->Property() & kIsStatic) { - if (strchr(cr->GetName(), '<')) - gInterpreter->ProcessLine(((std::string)cr->GetName()+"::"+m->GetName()+";").c_str()); - offset = (intptr_t)m->GetOffsetCint(); // yes, CINT (GetOffset() is both wrong - // and caches that wrong result! - if (offset == (intptr_t)-1) - return (intptr_t)gInterpreter->ProcessLine((std::string("&")+cr->GetName()+"::"+m->GetName()+";").c_str()); - } else - offset = (intptr_t)m->GetOffsetCint(); // yes, CINT, see above - return offset; - } - - return (intptr_t)-1; + return CppDispatch::GetVariableType(CppDispatch::GetUnderlyingScope(var)); } -static inline -Cppyy::TCppIndex_t gb2idx(TGlobal* gb) +std::string Cppyy::GetDatamemberTypeAsString(TCppScope_t scope) { - if (!gb) return (Cppyy::TCppIndex_t)-1; - - auto pidx = g_globalidx.find(gb); - if (pidx == g_globalidx.end()) { - auto idx = g_globalvars.size(); - g_globalvars.push_back(gb); - g_globalidx[gb] = idx; - return (Cppyy::TCppIndex_t)idx; - } - return (Cppyy::TCppIndex_t)pidx->second; -} - -Cppyy::TCppIndex_t Cppyy::GetDatamemberIndex(TCppScope_t scope, const std::string& name) -{ - if (scope == GLOBAL_HANDLE) { - TGlobal* gb = (TGlobal*)gROOT->GetListOfGlobals(false /* load */)->FindObject(name.c_str()); - if (!gb) gb = (TGlobal*)gROOT->GetListOfGlobals(true /* load */)->FindObject(name.c_str()); - if (!gb) { - // some enums are not loaded as they are not considered part of - // the global scope, but of the enum scope; get them w/o checking - TDictionary::DeclId_t did = gInterpreter->GetDataMember(nullptr, name.c_str()); - if (did) { - DataMemberInfo_t* t = gInterpreter->DataMemberInfo_Factory(did, nullptr); - ((TListOfDataMembers*)gROOT->GetListOfGlobals())->Get(t, true); - gb = (TGlobal*)gROOT->GetListOfGlobals(false /* load */)->FindObject(name.c_str()); - } - } - - if (gb && strcmp(gb->GetFullTypeName(), "(lambda)") == 0) { - // lambdas use a compiler internal closure type, so we wrap - // them, then return the wrapper's type - // TODO: this current leaks the std::function; also, if possible, - // should instantiate through TClass rather then ProcessLine - std::ostringstream s; - s << "auto __cppyy_internal_wrap_" << name << " = " - "new __cling_internal::FT::F" - "{" << name << "};"; - gInterpreter->ProcessLine(s.str().c_str()); - TGlobal* wrap = (TGlobal*)gROOT->GetListOfGlobals(true)->FindObject( - ("__cppyy_internal_wrap_"+name).c_str()); - if (wrap && wrap->GetAddress()) gb = wrap; - } - - return gb2idx(gb); - - } else { - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - TDataMember* dm = - (TDataMember*)cr->GetListOfDataMembers()->FindObject(name.c_str()); - // TODO: turning this into an index is silly ... - if (dm) return (TCppIndex_t)cr->GetListOfDataMembers()->IndexOf(dm); - } - } - - return (TCppIndex_t)-1; + return CppDispatch::GetTypeAsString( + CppDispatch::GetVariableType(CppDispatch::GetUnderlyingScope(scope))); } -Cppyy::TCppIndex_t Cppyy::GetDatamemberIndexEnumerated(TCppScope_t scope, TCppIndex_t idata) +std::string Cppyy::GetTypeAsString(TCppType_t type) { - if (scope == GLOBAL_HANDLE) { - TGlobal* gb = (TGlobal*)((THashList*)gROOT->GetListOfGlobals(false /* load */))->At((int)idata); - return gb2idx(gb); - } - - return idata; + return CppDispatch::GetTypeAsString(type); } +intptr_t Cppyy::GetDatamemberOffset(TCppScope_t var, TCppScope_t klass) +{ + return CppDispatch::GetVariableOffset(CppDispatch::GetUnderlyingScope(var), klass); +} // data member properties ---------------------------------------------------- -bool Cppyy::IsPublicData(TCppScope_t scope, TCppIndex_t idata) +bool Cppyy::IsPublicData(TCppScope_t datamem) { - if (scope == GLOBAL_HANDLE) - return true; - TClassRef& cr = type_from_handle(scope); - if (cr->Property() & kIsNamespace) - return true; - TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At((int)idata); - return m->Property() & kIsPublic; + return CppDispatch::IsPublicVariable(datamem); } -bool Cppyy::IsProtectedData(TCppScope_t scope, TCppIndex_t idata) +bool Cppyy::IsProtectedData(TCppScope_t datamem) { - if (scope == GLOBAL_HANDLE) - return true; - TClassRef& cr = type_from_handle(scope); - if (cr->Property() & kIsNamespace) - return true; - TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At((int)idata); - return m->Property() & kIsProtected; + return CppDispatch::IsProtectedVariable(datamem); } -bool Cppyy::IsStaticData(TCppScope_t scope, TCppIndex_t idata) +bool Cppyy::IsPrivateData(TCppScope_t datamem) { - if (scope == GLOBAL_HANDLE) - return true; - TClassRef& cr = type_from_handle(scope); - if (cr->Property() & kIsNamespace) - return true; - TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At((int)idata); - return m->Property() & kIsStatic; + return CppDispatch::IsPrivateVariable(datamem); } -bool Cppyy::IsConstData(TCppScope_t scope, TCppIndex_t idata) +bool Cppyy::IsStaticDatamember(TCppScope_t var) { - Long_t property = 0; - if (scope == GLOBAL_HANDLE) { - TGlobal* gbl = g_globalvars[idata]; - property = gbl->Property(); - } - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At((int)idata); - property = m->Property(); - } - -// if the data type is const, but the data member is a pointer/array, the data member -// itself is not const; alternatively it is a pointer that is constant - return ((property & kIsConstant) && !(property & (kIsPointer | kIsArray))) || (property & kIsConstPointer); + return CppDispatch::IsStaticVariable(CppDispatch::GetUnderlyingScope(var)); } -bool Cppyy::IsEnumData(TCppScope_t scope, TCppIndex_t idata) +bool Cppyy::IsConstVar(TCppScope_t var) { -// TODO: currently, ROOT/meta does not properly distinguish between variables of enum -// type, and values of enums. The latter are supposed to be const. This code relies on -// odd features (bugs?) to figure out the difference, but this should really be fixed -// upstream and/or deserves a new API. + return CppDispatch::IsConstVariable(var); +} - if (scope == GLOBAL_HANDLE) { - TGlobal* gbl = g_globalvars[idata]; +Cppyy::TCppScope_t Cppyy::ReduceReturnType(TCppScope_t fn, TCppType_t reduce) { + std::string fn_name = CppDispatch::GetQualifiedCompleteName(fn); + std::string signature = Cppyy::GetMethodSignature(fn, true); + std::string result_type = Cppyy::GetTypeAsString(reduce); - // make use of an oddity: enum global variables do not have their kIsStatic bit - // set, whereas enum global values do - return (gbl->Property() & kIsEnum) && (gbl->Property() & kIsStatic); + std::ostringstream call; + call << "("; + for (size_t i = 0, n = Cppyy::GetMethodNumArgs(fn); i < n; i++) { + call << Cppyy::GetMethodArgName(fn, i); + if (i != n - 1) + call << ", "; } - - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At((int)idata); - std::string ti = m->GetTypeName(); - - // can't check anonymous enums by type name, so just accept them as enums - if (ti.rfind("(anonymous)") != std::string::npos || ti.rfind("(unnamed)") != std::string::npos) - return m->Property() & kIsEnum; - - // since there seems to be no distinction between data of enum type and enum values, - // check the list of constants for the type to see if there's a match - if (ti.rfind(cr->GetName(), 0) != std::string::npos) { - std::string::size_type s = strlen(cr->GetName())+2; - if (s < ti.size()) { - TEnum* ee = ((TListOfEnums*)cr->GetListOfEnums())->GetObject(ti.substr(s, std::string::npos).c_str()); - if (ee) return ee->GetConstant(m->GetName()); - } - } + call << ")"; + + std::ostringstream code; + static int i = 0; + std::string name = "reduced_function_" + std::to_string(++i); + code << "namespace __cppyy_internal_wrap_g {\n" + << result_type << " " << name << signature << "{" << "return (" << result_type << ")::" << fn_name << call.str() << "; }\n" + << "}\n"; + if (Cppyy::Compile(code.str().c_str())) { + TCppScope_t res = CppDispatch::GetNamed(name, CppDispatch::GetScope("__cppyy_internal_wrap_g", nullptr)); + if (res) return res; } - -// this default return only means that the data will be writable, not that it will -// be unreadable or otherwise misrepresented - return false; + return fn; } -int Cppyy::GetDimensionSize(TCppScope_t scope, TCppIndex_t idata, int dimension) +std::vector Cppyy::GetDimensions(TCppType_t type) { - if (scope == GLOBAL_HANDLE) { - TGlobal* gbl = g_globalvars[idata]; - return gbl->GetMaxIndex(dimension); - } - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) { - TDataMember* m = (TDataMember*)cr->GetListOfDataMembers()->At((int)idata); - return m->GetMaxIndex(dimension); - } - return -1; + return CppDispatch::GetDimensions(type); } - // enum properties ----------------------------------------------------------- -Cppyy::TCppEnum_t Cppyy::GetEnum(TCppScope_t scope, const std::string& enum_name) +std::vector Cppyy::GetEnumConstants(TCppScope_t scope) { - if (scope == GLOBAL_HANDLE) - return (TCppEnum_t)gROOT->GetListOfEnums(kTRUE)->FindObject(enum_name.c_str()); - - TClassRef& cr = type_from_handle(scope); - if (cr.GetClass()) - return (TCppEnum_t)cr->GetListOfEnums(kTRUE)->FindObject(enum_name.c_str()); - - return (TCppEnum_t)0; + return CppDispatch::GetEnumConstants(scope); } -Cppyy::TCppIndex_t Cppyy::GetNumEnumData(TCppEnum_t etype) +Cppyy::TCppType_t Cppyy::GetEnumConstantType(TCppScope_t scope) { - return (TCppIndex_t)((TEnum*)etype)->GetConstants()->GetSize(); + return CppDispatch::GetEnumConstantType(CppDispatch::GetUnderlyingScope(scope)); } -std::string Cppyy::GetEnumDataName(TCppEnum_t etype, TCppIndex_t idata) +Cppyy::TCppIndex_t Cppyy::GetEnumDataValue(TCppScope_t scope) { - return ((TEnumConstant*)((TEnum*)etype)->GetConstants()->At((int)idata))->GetName(); + return CppDispatch::GetEnumConstantValue(scope); } -long long Cppyy::GetEnumDataValue(TCppEnum_t etype, TCppIndex_t idata) +Cppyy::TCppScope_t Cppyy::InstantiateTemplate( + TCppScope_t tmpl, CppDispatch::TemplateArgInfo* args, size_t args_size) { - TEnumConstant* ecst = (TEnumConstant*)((TEnum*)etype)->GetConstants()->At((int)idata); - return (long long)ecst->GetValue(); + return CppDispatch::InstantiateTemplate(tmpl, args, args_size, false); } - +void Cppyy::DumpScope(TCppScope_t scope) +{ + CppDispatch::DumpScope(scope); +} \ No newline at end of file diff --git a/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cpp_cppyy.h b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cpp_cppyy.h index 44056f8b71d95..cf654d82800b0 100644 --- a/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cpp_cppyy.h +++ b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cpp_cppyy.h @@ -2,13 +2,19 @@ #define CPYCPPYY_CPPYY_H // Standard +#include #include #include #include #include #include +#include +#include "callcontext.h" +#include "cppinterop_dispatch.h" // some more types; assumes Cppyy.h follows Python.h + +// using CppFinal #ifndef PY_LONG_LONG #ifdef _WIN32 typedef __int64 PY_LONG_LONG; @@ -29,54 +35,114 @@ typedef unsigned long long PY_ULONG_LONG; typedef long double PY_LONG_DOUBLE; #endif +typedef CPyCppyy::Parameter Parameter; -namespace Cppyy { - typedef size_t TCppScope_t; - typedef TCppScope_t TCppType_t; - typedef void* TCppEnum_t; - typedef void* TCppObject_t; - typedef intptr_t TCppMethod_t; +// small number that allows use of stack for argument passing +const int SMALL_ARGS_N = 8; - typedef size_t TCppIndex_t; - typedef void* TCppFuncAddr_t; +// convention to pass flag for direct calls (similar to Python's vector calls) +#define DIRECT_CALL ((size_t)1 << (8 * sizeof(size_t) - 1)) +static inline size_t CALL_NARGS(size_t nargs) { + return nargs & ~DIRECT_CALL; +} -// direct interpreter access ------------------------------------------------- +namespace Cppyy { + typedef Cpp::TCppScope_t TCppScope_t; + typedef Cpp::TCppType_t TCppType_t; + typedef Cpp::TCppScope_t TCppEnum_t; + typedef Cpp::TCppScope_t TCppObject_t; + typedef Cpp::TCppFunction_t TCppMethod_t; + typedef Cpp::TCppIndex_t TCppIndex_t; + typedef intptr_t TCppFuncAddr_t; + +// // direct interpreter access ------------------------------------------------- + // RPY_EXPORTED + // void AddSearchPath(const char* dir, bool isUser = true, bool prepend = false); RPY_EXPORTED bool Compile(const std::string& code, bool silent = false); RPY_EXPORTED std::string ToString(TCppType_t klass, TCppObject_t obj); - -// name to opaque C++ scope representation ----------------------------------- +// +// // name to opaque C++ scope representation ----------------------------------- RPY_EXPORTED std::string ResolveName(const std::string& cppitem_name); RPY_EXPORTED - std::string ResolveEnum(const std::string& enum_type); + TCppType_t ResolveType(TCppType_t cppitem_name); + RPY_EXPORTED + TCppType_t ResolveEnumReferenceType(TCppType_t type); + RPY_EXPORTED + TCppType_t ResolveEnumPointerType(TCppType_t type); + RPY_EXPORTED + TCppType_t GetRealType(TCppType_t type); + RPY_EXPORTED + TCppType_t GetPointerType(TCppType_t type); + RPY_EXPORTED + TCppType_t GetReferencedType(TCppType_t type, bool rvalue = false); + RPY_EXPORTED + std::string ResolveEnum(TCppScope_t enum_scope); + RPY_EXPORTED + bool IsClassType(TCppType_t type); + RPY_EXPORTED + bool IsPointerType(TCppType_t type); + RPY_EXPORTED + bool IsFunctionPointerType(TCppType_t type); + RPY_EXPORTED + TCppType_t GetType(const std::string &name, bool enable_slow_lookup = false); RPY_EXPORTED - TCppScope_t GetScope(const std::string& scope_name); + bool AppendTypesSlow(const std::string &name, + std::vector& types, Cppyy::TCppScope_t parent = nullptr); RPY_EXPORTED - TCppType_t GetActualClass(TCppType_t klass, TCppObject_t obj); + TCppType_t GetComplexType(const std::string &element_type); RPY_EXPORTED - size_t SizeOf(TCppType_t klass); + TCppScope_t GetScope(const std::string& scope_name, + TCppScope_t parent_scope = 0); RPY_EXPORTED - size_t SizeOf(const std::string& type_name); + TCppScope_t GetUnderlyingScope(TCppScope_t scope); + RPY_EXPORTED + TCppScope_t GetFullScope(const std::string& scope_name); + RPY_EXPORTED + TCppScope_t GetTypeScope(TCppScope_t klass); + RPY_EXPORTED + TCppScope_t GetNamed(const std::string& scope_name, + TCppScope_t parent_scope = 0); + RPY_EXPORTED + TCppScope_t GetParentScope(TCppScope_t scope); + RPY_EXPORTED + TCppScope_t GetScopeFromType(TCppType_t type); + RPY_EXPORTED + TCppType_t GetTypeFromScope(TCppScope_t klass); + RPY_EXPORTED + TCppScope_t GetGlobalScope(); + RPY_EXPORTED + TCppScope_t GetActualClass(TCppScope_t klass, TCppObject_t obj); + RPY_EXPORTED + size_t SizeOf(TCppScope_t klass); + RPY_EXPORTED + size_t SizeOfType(TCppType_t type); + RPY_EXPORTED + size_t SizeOf(const std::string &type) { assert(0 && "SizeOf"); return 0; } RPY_EXPORTED bool IsBuiltin(const std::string& type_name); + RPY_EXPORTED - bool IsComplete(const std::string& type_name); + bool IsBuiltin(TCppType_t type); RPY_EXPORTED - TCppScope_t gGlobalScope; // for fast access + bool IsComplete(TCppScope_t type); -// memory management --------------------------------------------------------- +// RPY_EXPORTED +// inline TCppScope_t gGlobalScope = 0; // for fast access +// +// // memory management --------------------------------------------------------- RPY_EXPORTED - TCppObject_t Allocate(TCppType_t type); + TCppObject_t Allocate(TCppScope_t scope); RPY_EXPORTED - void Deallocate(TCppType_t type, TCppObject_t instance); + void Deallocate(TCppScope_t scope, TCppObject_t instance); RPY_EXPORTED - TCppObject_t Construct(TCppType_t type, void* arena = nullptr); + TCppObject_t Construct(TCppScope_t scope, void* arena = nullptr); RPY_EXPORTED - void Destruct(TCppType_t type, TCppObject_t instance); + void Destruct(TCppScope_t scope, TCppObject_t instance); // method/function dispatching ----------------------------------------------- RPY_EXPORTED @@ -105,16 +171,16 @@ namespace Cppyy { RPY_EXPORTED char* CallS(TCppMethod_t method, TCppObject_t self, size_t nargs, void* args, size_t* length); RPY_EXPORTED - TCppObject_t CallConstructor(TCppMethod_t method, TCppType_t type, size_t nargs, void* args); + TCppObject_t CallConstructor(TCppMethod_t method, TCppScope_t klass, size_t nargs, void* args); RPY_EXPORTED - void CallDestructor(TCppType_t type, TCppObject_t self); + void CallDestructor(TCppScope_t type, TCppObject_t self); RPY_EXPORTED TCppObject_t CallO(TCppMethod_t method, TCppObject_t self, size_t nargs, void* args, TCppType_t result_type); RPY_EXPORTED TCppFuncAddr_t GetFunctionAddress(TCppMethod_t method, bool check_enabled=true); -// handling of function argument buffer -------------------------------------- +// // handling of function argument buffer -------------------------------------- RPY_EXPORTED void* AllocateFunctionArgs(size_t nargs); RPY_EXPORTED @@ -124,28 +190,40 @@ namespace Cppyy { RPY_EXPORTED size_t GetFunctionArgTypeoffset(); -// scope reflection information ---------------------------------------------- +// // scope reflection information ---------------------------------------------- RPY_EXPORTED bool IsNamespace(TCppScope_t scope); RPY_EXPORTED - bool IsTemplate(const std::string& template_name); + bool IsClass(TCppScope_t scope); + RPY_EXPORTED + bool IsTemplate(TCppScope_t scope); + RPY_EXPORTED + bool IsTemplateInstantiation(TCppScope_t scope); RPY_EXPORTED - bool IsAbstract(TCppType_t type); + bool IsTypedefed(TCppScope_t scope); RPY_EXPORTED - bool IsEnum(const std::string& type_name); + bool IsAbstract(TCppScope_t scope); + RPY_EXPORTED + bool IsEnumScope(TCppScope_t scope); + RPY_EXPORTED + bool IsEnumConstant(TCppScope_t scope); + RPY_EXPORTED + bool IsEnumType(TCppType_t type); RPY_EXPORTED bool IsAggregate(TCppType_t type); RPY_EXPORTED - bool IsDefaultConstructable(TCppType_t type); + bool IsDefaultConstructable(TCppScope_t scope); + RPY_EXPORTED + bool IsVariable(TCppScope_t scope); RPY_EXPORTED void GetAllCppNames(TCppScope_t scope, std::set& cppnames); -// namespace reflection information ------------------------------------------ +// // namespace reflection information ------------------------------------------ RPY_EXPORTED - std::vector GetUsingNamespaces(TCppScope_t); - -// class reflection information ---------------------------------------------- + std::vector GetUsingNamespaces(TCppScope_t); +// +// // class reflection information ---------------------------------------------- RPY_EXPORTED std::string GetFinalName(TCppType_t type); RPY_EXPORTED @@ -153,47 +231,53 @@ namespace Cppyy { RPY_EXPORTED bool HasVirtualDestructor(TCppType_t type); RPY_EXPORTED - bool HasComplexHierarchy(TCppType_t type); + bool HasComplexHierarchy(TCppType_t type) { assert(0 && "HasComplexHierarchy"); return false; } + RPY_EXPORTED + TCppIndex_t GetNumBases(TCppScope_t klass); RPY_EXPORTED - TCppIndex_t GetNumBases(TCppType_t type); + TCppIndex_t GetNumBasesLongestBranch(TCppScope_t klass); RPY_EXPORTED - TCppIndex_t GetNumBasesLongestBranch(TCppType_t type); + std::string GetBaseName(TCppScope_t klass, TCppIndex_t ibase); RPY_EXPORTED - std::string GetBaseName(TCppType_t type, TCppIndex_t ibase); + TCppScope_t GetBaseScope(TCppScope_t klass, TCppIndex_t ibase); RPY_EXPORTED - bool IsSubtype(TCppType_t derived, TCppType_t base); + bool IsSubclass(TCppType_t derived, TCppType_t base); RPY_EXPORTED - bool IsSmartPtr(TCppType_t type); + bool IsSmartPtr(TCppScope_t klass); RPY_EXPORTED bool GetSmartPtrInfo(const std::string&, TCppType_t* raw, TCppMethod_t* deref); RPY_EXPORTED - void AddSmartPtrType(const std::string&); + void AddSmartPtrType(const std::string&) { assert(0 && "AddSmartPtrType"); return; } RPY_EXPORTED - void AddTypeReducer(const std::string& reducable, const std::string& reduced); + void AddTypeReducer(const std::string& reducable, const std::string& reduced) { assert(0 && "AddTypeReducer"); return; } -// calculate offsets between declared and actual type, up-cast: direction > 0; down-cast: direction < 0 +// // calculate offsets between declared and actual type, up-cast: direction > 0; down-cast: direction < 0 RPY_EXPORTED ptrdiff_t GetBaseOffset( TCppType_t derived, TCppType_t base, TCppObject_t address, int direction, bool rerror = false); -// method/function reflection information ------------------------------------ +// // method/function reflection information ------------------------------------ RPY_EXPORTED - TCppIndex_t GetNumMethods(TCppScope_t scope, bool accept_namespace = false); + void GetClassMethods(TCppScope_t scope, std::vector &methods); RPY_EXPORTED - std::vector GetMethodIndicesFromName(TCppScope_t scope, const std::string& name); + std::vector GetMethodsFromName(TCppScope_t scope, + const std::string& name); RPY_EXPORTED - TCppMethod_t GetMethod(TCppScope_t scope, TCppIndex_t imeth); + TCppMethod_t GetMethod(TCppScope_t scope, TCppIndex_t imeth) { return 0; } RPY_EXPORTED std::string GetMethodName(TCppMethod_t); RPY_EXPORTED std::string GetMethodFullName(TCppMethod_t); + // GetMethodMangledName is unused. + RPY_EXPORTED + std::string GetMethodMangledName(TCppMethod_t) { assert(0 && "GetMethodMangledName"); return ""; } RPY_EXPORTED - std::string GetMethodMangledName(TCppMethod_t); + TCppType_t GetMethodReturnType(TCppMethod_t); RPY_EXPORTED - std::string GetMethodResultType(TCppMethod_t); + std::string GetMethodReturnTypeAsString(TCppMethod_t); RPY_EXPORTED TCppIndex_t GetMethodNumArgs(TCppMethod_t); RPY_EXPORTED @@ -201,90 +285,152 @@ namespace Cppyy { RPY_EXPORTED std::string GetMethodArgName(TCppMethod_t, TCppIndex_t iarg); RPY_EXPORTED - std::string GetMethodArgType(TCppMethod_t, TCppIndex_t iarg); + TCppType_t GetMethodArgType(TCppMethod_t, TCppIndex_t iarg); RPY_EXPORTED TCppIndex_t CompareMethodArgType(TCppMethod_t, TCppIndex_t iarg, const std::string &req_type); RPY_EXPORTED + std::string GetMethodArgTypeAsString(TCppMethod_t method, TCppIndex_t iarg); + RPY_EXPORTED + std::string GetMethodArgCanonTypeAsString(TCppMethod_t method, TCppIndex_t iarg); + RPY_EXPORTED std::string GetMethodArgDefault(TCppMethod_t, TCppIndex_t iarg); RPY_EXPORTED - std::string GetMethodSignature(TCppMethod_t, bool show_formalargs, TCppIndex_t maxargs = (TCppIndex_t)-1); + std::string GetMethodSignature(TCppMethod_t, bool show_formal_args, TCppIndex_t max_args = (TCppIndex_t)-1); + // GetMethodPrototype is unused. RPY_EXPORTED - std::string GetMethodPrototype(TCppScope_t scope, TCppMethod_t, bool show_formalargs); + std::string GetMethodPrototype(TCppMethod_t, bool show_formal_args); RPY_EXPORTED bool IsConstMethod(TCppMethod_t); - +// // Templated method/function reflection information ------------------------------------ + RPY_EXPORTED + void GetTemplatedMethods(TCppScope_t scope, std::vector &methods); RPY_EXPORTED TCppIndex_t GetNumTemplatedMethods(TCppScope_t scope, bool accept_namespace = false); RPY_EXPORTED std::string GetTemplatedMethodName(TCppScope_t scope, TCppIndex_t imeth); RPY_EXPORTED - bool IsTemplatedConstructor(TCppScope_t scope, TCppIndex_t imeth); - RPY_EXPORTED bool ExistsMethodTemplate(TCppScope_t scope, const std::string& name); RPY_EXPORTED - bool IsStaticTemplate(TCppScope_t scope, const std::string& name); + bool IsTemplatedMethod(TCppMethod_t method); RPY_EXPORTED - bool IsMethodTemplate(TCppScope_t scope, TCppIndex_t imeth); + bool IsStaticTemplate(TCppScope_t scope, const std::string& name); RPY_EXPORTED TCppMethod_t GetMethodTemplate( TCppScope_t scope, const std::string& name, const std::string& proto); - RPY_EXPORTED - TCppIndex_t GetGlobalOperator( + void GetClassOperators(Cppyy::TCppScope_t klass, const std::string& opname, + std::vector& operators); + RPY_EXPORTED + TCppMethod_t GetGlobalOperator( TCppType_t scope, const std::string& lc, const std::string& rc, const std::string& op); // method properties --------------------------------------------------------- + RPY_EXPORTED + bool IsDeletedMethod(TCppMethod_t method); RPY_EXPORTED bool IsPublicMethod(TCppMethod_t method); RPY_EXPORTED bool IsProtectedMethod(TCppMethod_t method); RPY_EXPORTED + bool IsPrivateMethod(TCppMethod_t method); + RPY_EXPORTED bool IsConstructor(TCppMethod_t method); RPY_EXPORTED bool IsDestructor(TCppMethod_t method); RPY_EXPORTED bool IsStaticMethod(TCppMethod_t method); - RPY_EXPORTED - bool IsExplicit(TCppMethod_t method); -// data member reflection information ---------------------------------------- +// // data member reflection information ---------------------------------------- + // GetNumDatamembers is unused. + // RPY_EXPORTED + // TCppIndex_t GetNumDatamembers(TCppScope_t scope, bool accept_namespace = false) { return 0; } + RPY_EXPORTED + void GetDatamembers(TCppScope_t scope, std::vector& datamembers); + // GetDatamemberName is unused. + // RPY_EXPORTED + // std::string GetDatamemberName(TCppScope_t scope, TCppIndex_t idata) { return ""; } + RPY_EXPORTED + bool IsLambdaClass(TCppType_t type); RPY_EXPORTED - TCppIndex_t GetNumDatamembers(TCppScope_t scope, bool accept_namespace = false); + TCppScope_t WrapLambdaFromVariable(TCppScope_t var); RPY_EXPORTED - std::string GetDatamemberName(TCppScope_t scope, TCppIndex_t idata); + TCppScope_t AdaptFunctionForLambdaReturn(TCppScope_t fn); RPY_EXPORTED - std::string GetDatamemberType(TCppScope_t scope, TCppIndex_t idata); + TCppType_t GetDatamemberType(TCppScope_t data); RPY_EXPORTED - intptr_t GetDatamemberOffset(TCppScope_t scope, TCppIndex_t idata); + std::string GetDatamemberTypeAsString(TCppScope_t var); RPY_EXPORTED - TCppIndex_t GetDatamemberIndex(TCppScope_t scope, const std::string& name); + std::string GetTypeAsString(TCppType_t type); RPY_EXPORTED - TCppIndex_t GetDatamemberIndexEnumerated(TCppScope_t scope, TCppIndex_t idata); + intptr_t GetDatamemberOffset(TCppScope_t var, TCppScope_t klass = nullptr); + RPY_EXPORTED + bool CheckDatamember(TCppScope_t scope, const std::string& name); -// data member properties ---------------------------------------------------- +// // data member properties ---------------------------------------------------- + RPY_EXPORTED + bool IsPublicData(TCppScope_t var); RPY_EXPORTED - bool IsPublicData(TCppScope_t scope, TCppIndex_t idata); + bool IsProtectedData(TCppScope_t var); RPY_EXPORTED - bool IsProtectedData(TCppScope_t scope, TCppIndex_t idata); + bool IsPrivateData(TCppScope_t var); RPY_EXPORTED - bool IsStaticData(TCppScope_t scope, TCppIndex_t idata); + bool IsStaticDatamember(TCppScope_t var); RPY_EXPORTED - bool IsConstData(TCppScope_t scope, TCppIndex_t idata); + bool IsConstVar(TCppScope_t var); RPY_EXPORTED - bool IsEnumData(TCppScope_t scope, TCppIndex_t idata); + TCppScope_t ReduceReturnType(TCppScope_t fn, TCppType_t reduce); +// RPY_EXPORTED +// bool IsEnumData(TCppScope_t scope, TCppIndex_t idata); RPY_EXPORTED - int GetDimensionSize(TCppScope_t scope, TCppIndex_t idata, int dimension); + std::vector GetDimensions(TCppType_t type); -// enum properties ----------------------------------------------------------- +// // enum properties ----------------------------------------------------------- + // GetEnum is unused. + // RPY_EXPORTED + // TCppEnum_t GetEnum(TCppScope_t scope, const std::string& enum_name) { return 0; } RPY_EXPORTED - TCppEnum_t GetEnum(TCppScope_t scope, const std::string& enum_name); + std::vector GetEnumConstants(TCppScope_t scope); + // GetEnumDataName is unused. + // RPY_EXPORTED + // std::string GetEnumDataName(TCppEnum_t, TCppIndex_t idata) { return ""; } RPY_EXPORTED - TCppIndex_t GetNumEnumData(TCppEnum_t); + TCppType_t GetEnumConstantType(TCppScope_t scope); RPY_EXPORTED - std::string GetEnumDataName(TCppEnum_t, TCppIndex_t idata); + TCppIndex_t GetEnumDataValue(TCppScope_t scope); + RPY_EXPORTED - long long GetEnumDataValue(TCppEnum_t, TCppIndex_t idata); + TCppScope_t InstantiateTemplate( + TCppScope_t tmpl, Cpp::TemplateArgInfo* args, size_t args_size); + RPY_EXPORTED + void DumpScope(TCppScope_t scope); } // namespace Cppyy + +// class CallWrapper { +// public: +// typedef const void* DeclId_t; +// +// public: +// CallWrapper(void * f) : fDecl(nullptr), fName(""), fTF(nullptr) { assert(0); } +// CallWrapper(DeclId_t fid, const std::string& n) : fDecl(fid), fName(n), fTF(nullptr) {} +// ~CallWrapper() {} +// +// public: +// cling::Interpreter::CallFuncIFacePtr_t fFaceptr; +// DeclId_t fDecl; +// std::string fName; +// void * fTF; +// }; +// +// +// +// inline std::vector gWrapperHolder; +// +// inline +// CallWrapper* new_CallWrapper(CallWrapper::DeclId_t fid, const std::string& n); +// +// inline +// bool WrapperCall(Cppyy::TCppMethod_t method, size_t nargs, void* args_, void* self, void* result); + #endif // !CPYCPPYY_CPPYY_H diff --git a/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cppinterop_dispatch.cxx b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cppinterop_dispatch.cxx new file mode 100644 index 0000000000000..66425c171df90 --- /dev/null +++ b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cppinterop_dispatch.cxx @@ -0,0 +1,17 @@ +#include "cppinterop_dispatch.h" + + +#define DECLARE_CPP_NULL(func_name) \ + CppAPIType::func_name func_name = nullptr; + + // Worker macros for different operations +#define DECLARE_EXTERN_FUNC(func_name, ...) \ + extern CppAPIType::func_name func_name; + +#define LOAD_FUNCTION(func_name, ...) \ + func_name = reinterpret_cast(dlGetProcAddress(#func_name)); + + +namespace CppDispatch { + FOR_EACH_CPP_FUNCTION(DECLARE_CPP_NULL); +} diff --git a/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cppinterop_dispatch.h b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cppinterop_dispatch.h new file mode 100644 index 0000000000000..e82ac7e110c60 --- /dev/null +++ b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cppinterop_dispatch.h @@ -0,0 +1,56 @@ +#ifndef CPPINTEROP_DISPATCH_H +#define CPPINTEROP_DISPATCH_H + +#include +#include +#include +#include +#include +#include +#include +#include "CppInterOp/CppInterOpDispatch.h" + + +static inline void* dlGetProcAddress(const char* name, const char* libPath = "/home/ajomy/ROOT/root_build/lib/libCling.so") +{ + if (!name) return nullptr; + + static std::once_flag loaded; + static void* handle = nullptr; + static void* (*getCppProcAddress)(const char*) = nullptr; + static bool initialization_successful = false; + + // Initialize the library once + std::call_once(loaded, [libPath]() { + handle = dlopen(libPath, RTLD_LOCAL | RTLD_NOW); + if (!handle) { + std::cout << "Failed to open library: " << dlerror() << std::endl; + return; + } + + getCppProcAddress = reinterpret_cast(dlsym(handle, "CppGetProcAddress")); + if (!getCppProcAddress) { + std::cout << "Failed to find CppGetProcAddress: " << dlerror() << std::endl; + dlclose(handle); + handle = nullptr; + return; + } + + initialization_successful = true; + }); + if (!initialization_successful || !getCppProcAddress) { + return nullptr; + } + // std::cout<<"Getting address for function: "<; placed here because it's simpler to write the # custom "npos" object (to allow easy result checking of find/rfind) in Python - elif pyclass.__cpp_name__ == "std::string": - class NPOS(0x3000000 <= sys.hexversion and int or long): + elif pyclass.__cpp_name__ == "std::basic_string": + class NPOS(int): + def __init__(self, npos): + self.__cpp_npos = npos def __eq__(self, other): - return other == -1 or int(self) == other + return other == -1 or other == self.__cpp_npos def __ne__(self, other): - return other != -1 and int(self) != other - del pyclass.__class__.npos # drop b/c is const data + return other != -1 and other != self.__cpp_npos + if hasattr(pyclass.__class__, 'npos'): + del pyclass.__class__.npos # drop b/c is const data pyclass.npos = NPOS(pyclass.npos) return True @@ -158,24 +178,24 @@ def __getitem__(self, cls): return py_make_smartptr(cls, self.ptrcls) except AttributeError: pass - if isinstance(cls, str) and not cls in ('int', 'float'): + if type(cls) == str and not cls in ('int', 'float'): return py_make_smartptr(getattr(gbl, cls), self.ptrcls) return self.maker[cls] -gbl.std.make_shared = make_smartptr(gbl.std.shared_ptr, gbl.std.make_shared) -gbl.std.make_unique = make_smartptr(gbl.std.unique_ptr, gbl.std.make_unique) +# gbl.std.make_shared = make_smartptr(gbl.std.shared_ptr, gbl.std.make_shared) +# gbl.std.make_unique = make_smartptr(gbl.std.unique_ptr, gbl.std.make_unique) del make_smartptr #--- interface to Cling ------------------------------------------------------ class _stderr_capture(object): def __init__(self): - self._capture = not gbl.gDebug and True or False - self.err = "" + self._capture = not gbl.Cpp.IsDebugOutputEnabled() + self.err = "" def __enter__(self): if self._capture: - _begin_capture_stderr() + _begin_capture_stderr() return self def __exit__(self, tp, val, trace): @@ -200,8 +220,12 @@ def _cling_report(msg, errcode, msg_is_error=False): def cppdef(src): """Declare C++ source to Cling.""" with _stderr_capture() as err: - errcode = gbl.gInterpreter.Declare(src) - _cling_report(err.err, int(not errcode), msg_is_error=True) + errcode = gbl.Cpp.Declare(src, False) + if not errcode == 0 or err.err: + if 'warning' in err.err.lower() and not 'error' in err.err.lower(): + warnings.warn(err.err, SyntaxWarning) + return True + raise SyntaxError('Failed to parse the given C++ code%s' % err.err) return True def cppexec(stmt): @@ -209,23 +233,26 @@ def cppexec(stmt): if stmt and stmt[-1] != ';': stmt += ';' - # capture stderr, but note that ProcessLine could legitimately be writing to + # capture stderr, but note that Process could legitimately be writing to # std::cerr, in which case the captured output needs to be printed as normal with _stderr_capture() as err: errcode = ctypes.c_int(0) try: - gbl.gInterpreter.ProcessLine(stmt, ctypes.pointer(errcode)) + errcode = gbl.Cpp.Process(stmt) except Exception as e: sys.stderr.write("%s\n\n" % str(e)) - if not errcode.value: - errcode.value = 1 + if not errcode.value: errcode.value = 1 - _cling_report(err.err, errcode.value) - if err.err and err.err[1:] != '\n': + if not errcode == 0: + raise SyntaxError('Failed to parse the given C++ code%s' % err.err) + elif err.err and err.err[1:] != '\n': sys.stderr.write(err.err[1:]) return True +def evaluate(input, HadError = _backend.nullptr): + return gbl.Cpp.Evaluate(input, HadError) + def macro(cppm): """Attempt to evalute a C/C++ pre-processor macro as a constant""" @@ -243,35 +270,27 @@ def macro(cppm): def load_library(name): """Explicitly load a shared library.""" with _stderr_capture() as err: - gSystem = gbl.gSystem - if name[:3] != 'lib': - if not gSystem.FindDynamicLibrary(gbl.TString(name), True) and\ - gSystem.FindDynamicLibrary(gbl.TString('lib'+name), True): - name = 'lib'+name - sc = gSystem.Load(name) - if sc == -1: - # special case for Windows as of python3.8: use winmode=0, otherwise the default - # will not consider regular search paths (such as $PATH) - if 0x3080000 <= sys.hexversion and 'win32' in sys.platform and os.path.isabs(name): - return ctypes.CDLL(name, ctypes.RTLD_GLOBAL, winmode=0) # raises on error - raise RuntimeError('Unable to load library "%s"%s' % (name, err.err)) + result = gbl.Cpp.LoadLibrary(name, True) + if result == False: + raise RuntimeError('Could not load library "%s": %s' % (name, err.err)) + return True def include(header): """Load (and JIT) header file
into Cling.""" with _stderr_capture() as err: - errcode = gbl.gInterpreter.Declare('#include "%s"' % header) - if not errcode: + errcode = gbl.Cpp.Declare('#include "%s"' % header, False) + if not errcode == 0: raise ImportError('Failed to load header file "%s"%s' % (header, err.err)) return True def c_include(header): """Load (and JIT) header file
into Cling.""" with _stderr_capture() as err: - errcode = gbl.gInterpreter.Declare("""extern "C" { -#include "%s" -}""" % header) - if not errcode: + errcode = gbl.Cpp.Declare("""extern "C" { + #include "%s" + }""" % header, False) + if not errcode == 0: raise ImportError('Failed to load header file "%s"%s' % (header, err.err)) return True @@ -279,7 +298,7 @@ def add_include_path(path): """Add a path to the include paths available to Cling.""" if not os.path.isdir(path): raise OSError('No such directory: %s' % path) - gbl.gInterpreter.AddIncludePath(path) + gbl.Cpp.AddIncludePath(path) def add_library_path(path): """Add a path to the library search paths available to Cling.""" @@ -319,23 +338,22 @@ def add_library_path(path): else: import pkg_resources as pr - d = pr.get_distribution('CPyCppyy') - for line in d.get_metadata_lines('RECORD'): - if 'API.h' in line: - ape = os.path.join(d.location, line[0:line.find(',')]) - break - del line, d, pr + d = pr.get_distribution('CPyCppyy') + for line in d.get_metadata_lines('RECORD'): + if 'API.h' in line: + part = line[0:line.find(',')] + ape = os.path.join(d.location, part) if os.path.exists(ape): apipath_extra = os.path.dirname(os.path.dirname(ape)) - del ape + + del part, d, pr except Exception: pass if apipath_extra is None: ldversion = sysconfig.get_config_var('LDVERSION') - if not ldversion: - ldversion = sys.version[:3] + if not ldversion: ldversion = sys.version[:3] apipath_extra = os.path.join(os.path.dirname(apipath), 'site', 'python'+ldversion) if not os.path.exists(os.path.join(apipath_extra, 'CPyCppyy')): @@ -361,9 +379,7 @@ def add_library_path(path): if apipath_extra.lower() != 'none': if not os.path.exists(os.path.join(apipath_extra, 'CPyCppyy')): - warnings.warn("CPyCppyy API not found (tried: %s); " - "set CPPYY_API_PATH envar to the 'CPyCppyy' API directory to fix" - % apipath_extra) + warnings.warn("CPyCppyy API not found (tried: %s); set CPPYY_API_PATH envar to the 'CPyCppyy' API directory to fix" % apipath_extra) else: add_include_path(apipath_extra) @@ -372,20 +388,15 @@ def add_library_path(path): if os.getenv('CONDA_PREFIX'): # MacOS, Linux include_path = os.path.join(os.getenv('CONDA_PREFIX'), 'include') - if os.path.exists(include_path): - add_include_path(include_path) + if os.path.exists(include_path): add_include_path(include_path) # Windows include_path = os.path.join(os.getenv('CONDA_PREFIX'), 'Library', 'include') - if os.path.exists(include_path): - add_include_path(include_path) + if os.path.exists(include_path): add_include_path(include_path) -# assuming that we are in PREFIX/lib/python/site-packages/cppyy, -# add PREFIX/include to the search path -include_path = os.path.abspath( - os.path.join(os.path.dirname(__file__), *(4*[os.path.pardir]+['include']))) -if os.path.exists(include_path): - add_include_path(include_path) +# assuming that we are in PREFIX/lib/python/site-packages/cppyy, add PREFIX/include to the search path +include_path = os.path.abspath(os.path.join(os.path.dirname(__file__), *(4*[os.path.pardir]+['include']))) +if os.path.exists(include_path): add_include_path(include_path) del include_path, apipath, ispypy @@ -393,17 +404,14 @@ def add_autoload_map(fname): """Add the entries from a autoload (.rootmap) file to Cling.""" if not os.path.isfile(fname): raise OSError("no such file: %s" % fname) - gbl.gInterpreter.LoadLibraryMap(fname) + gbl.cling.runtime.gCling.LoadLibraryMap(fname) def set_debug(enable=True): """Enable/disable debug output.""" - if enable: - gbl.gDebug = 10 - else: - gbl.gDebug = 0 + gbl.Cpp.EnableDebugOutput(enable) def _get_name(tt): - if isinstance(tt, str): + if type(tt) == str: return tt try: ttname = tt.__cpp_name__ @@ -414,7 +422,7 @@ def _get_name(tt): _sizes = {} def sizeof(tt): """Returns the storage size (in chars) of C++ type .""" - if not isinstance(tt, type) and not isinstance(tt, str): + if not isinstance(tt, type) and not type(tt) == str: tt = type(tt) try: return _sizes[tt] @@ -422,7 +430,7 @@ def sizeof(tt): try: sz = ctypes.sizeof(tt) except TypeError: - sz = gbl.gInterpreter.ProcessLine("sizeof(%s);" % (_get_name(tt),)) + sz = gbl.Cpp.Evaluate("sizeof(%s)" % (_get_name(tt),), nullptr) _sizes[tt] = sz return sz @@ -435,7 +443,7 @@ def typeid(tt): return _typeids[tt] except KeyError: tidname = 'typeid_'+str(len(_typeids)) - gbl.gInterpreter.ProcessLine( + cppexec( "namespace _cppyy_internal { auto* %s = &typeid(%s); }" %\ (tidname, _get_name(tt),)) tid = getattr(gbl._cppyy_internal, tidname) @@ -445,9 +453,15 @@ def typeid(tt): def multi(*bases): # after six, see also _typemap.py """Resolve metaclasses for multiple inheritance.""" # contruct a "no conflict" meta class; the '_meta' is needed by convention - nc_meta = type.__new__( - type, 'cppyy_nc_meta', tuple(type(b) for b in bases if type(b) is not type), {}) + nc_meta = type.__new__(type, 'cppyy_nc_meta', tuple(type(b) for b in bases if type(b) is not type), {}) class faux_meta(type): - def __new__(mcs, name, this_bases, d): + def __new__(cls, name, this_bases, d): return nc_meta(name, bases, d) return type.__new__(faux_meta, 'faux_meta', (), {}) + + +#- workaround (TODO: may not be needed with Clang9) -------------------------- +if 'win32' in sys.platform: + cppdef("""template<> + std::basic_ostream>& __cdecl std::endl>( + std::basic_ostream>&);""") diff --git a/bindings/pyroot/cppyy/cppyy/python/cppyy/__pyinstaller/hook-cppyy.py b/bindings/pyroot/cppyy/cppyy/python/cppyy/__pyinstaller/hook-cppyy.py index 4819b6ab00b01..f7d0c6305cd41 100644 --- a/bindings/pyroot/cppyy/cppyy/python/cppyy/__pyinstaller/hook-cppyy.py +++ b/bindings/pyroot/cppyy/cppyy/python/cppyy/__pyinstaller/hook-cppyy.py @@ -21,7 +21,7 @@ def datafile(path): def _api_files(): import cppyy, os - paths = str(cppyy.gbl.gInterpreter.GetIncludePath()).split('-I') + paths = str(cppyy.gbl.gCling.GetIncludePath()).split('-I') for p in paths: if not p: continue diff --git a/bindings/pyroot/cppyy/cppyy/python/cppyy/_cpython_cppyy.py b/bindings/pyroot/cppyy/cppyy/python/cppyy/_cpython_cppyy.py index f002931b0e717..a51feede961c3 100644 --- a/bindings/pyroot/cppyy/cppyy/python/cppyy/_cpython_cppyy.py +++ b/bindings/pyroot/cppyy/cppyy/python/cppyy/_cpython_cppyy.py @@ -1,10 +1,8 @@ """ CPython-specific touch-ups """ -import ctypes -import sys - from . import _stdcpp_fix +from cppyy_backend import loader __all__ = [ 'gbl', @@ -33,10 +31,12 @@ _backend._cpp_backend = c # explicitly expose APIs from libcppyy +import ctypes _w = ctypes.CDLL(_backend.__file__, ctypes.RTLD_GLOBAL) # some beautification for inspect (only on p2) +import sys if sys.hexversion < 0x3000000: # TODO: this reliese on CPPOverload cooking up a func_code object, which atm # is simply not implemented for p3 :/ @@ -68,10 +68,11 @@ class Template(object): # expected/used by ProxyWrappers.cxx in CPyCppyy stl_fixed_size_types = ['std::array'] stl_mapping_types = ['std::map', 'std::unordered_map'] - def __init__(self, name): + def __init__(self, name, scope): self.__name__ = name self.__cpp_name__ = name self._instantiations = dict() + self.__scope__ = scope def __repr__(self): return "" % (self.__name__, hex(id(self))) @@ -88,7 +89,7 @@ def __getitem__(self, *args): pass # construct the type name from the types or their string representation - newargs = [self.__name__] + newargs = [self.__scope__] for arg in args: if isinstance(arg, str): arg = ','.join(map(lambda x: x.strip(), arg.split(','))) @@ -103,13 +104,11 @@ def __getitem__(self, *args): if 'reserve' in pyclass.__dict__: def iadd(self, ll): self.reserve(len(ll)) - for x in ll: - self.push_back(x) + for x in ll: self.push_back(x) return self else: def iadd(self, ll): - for x in ll: - self.push_back(x) + for x in ll: self.push_back(x) return self pyclass.__iadd__ = iadd @@ -124,7 +123,7 @@ def __call__(self, *args): # most common cases are covered if args: args0 = args[0] - if args0 and isinstance(args0, (tuple, list)): + if args0 and (type(args0) is tuple or type(args0) is list): t = type(args0[0]) if t is float: t = 'double' @@ -135,7 +134,7 @@ def __call__(self, *args): if self.__name__ in self.stl_unrolled_types: return self[tuple(type(a) for a in args0)](*args0) - if args0 and isinstance(args0, dict): + if args0 and type(args0) is dict: if self.__name__ in self.stl_mapping_types: try: pair = args0.items().__iter__().__next__() @@ -161,32 +160,32 @@ def __call__(self, *args): gbl.std = _backend.CreateScopeProxy('std') # for move, we want our "pythonized" one, not the C++ template gbl.std.move = _backend.move +gbl.Cpp = gbl.CppDispatch #- add to the dynamic path as needed ----------------------------------------- import os def add_default_paths(): - gSystem = gbl.gSystem + libCppInterOp = gbl.CppDispatch if os.getenv('CONDA_PREFIX'): # MacOS, Linux lib_path = os.path.join(os.getenv('CONDA_PREFIX'), 'lib') - if os.path.exists(lib_path): gSystem.AddDynamicPath(lib_path) + if os.path.exists(lib_path): libCppInterOp.AddSearchPath(lib_path, True, False) # Windows lib_path = os.path.join(os.getenv('CONDA_PREFIX'), 'Library', 'lib') - if os.path.exists(lib_path): gSystem.AddDynamicPath(lib_path) + if os.path.exists(lib_path): libCppInterOp.AddSearchPath(lib_path, True, False) # assuming that we are in PREFIX/lib/python/site-packages/cppyy, add PREFIX/lib to the search path - lib_path = os.path.abspath( - os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir, os.path.pardir)) - if os.path.exists(lib_path): gSystem.AddDynamicPath(lib_path) + lib_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir, os.path.pardir)) + if os.path.exists(lib_path): libCppInterOp.AddSearchPath(lib_path, True, False) try: with open('/etc/ld.so.conf') as ldconf: for line in ldconf: f = line.strip() if (os.path.exists(f)): - gSystem.AddDynamicPath(f) + libCppInterOp.AddSearchPath(f, True, False) except IOError: pass add_default_paths() @@ -200,9 +199,18 @@ def add_default_paths(): default = _backend.default def load_reflection_info(name): - sc = gbl.gSystem.Load(name) - if sc == -1: - raise RuntimeError("Unable to load reflection library "+name) +# with _stderr_capture() as err: + #FIXME: Remove the .so and add logic in libcppinterop + name = name + ".so" + result = gbl.CppDispatch.LoadLibrary(name, True) + if name.endswith("Dict.so"): + header = name[:-7] + ".h" + gbl.CppDispatch.Declare('#include "' + header +'"', False) + + if result == False: + raise RuntimeError('Could not load library "%s"' % (name)) + + return True def _begin_capture_stderr(): _backend._begin_capture_stderr() diff --git a/bindings/pyroot/cppyy/cppyy/test/assert_interactive.py b/bindings/pyroot/cppyy/cppyy/test/assert_interactive.py index c3bd515720c61..683a9c1f1f79f 100644 --- a/bindings/pyroot/cppyy/cppyy/test/assert_interactive.py +++ b/bindings/pyroot/cppyy/cppyy/test/assert_interactive.py @@ -14,4 +14,4 @@ assert g.std except ImportError: # full lazy lookup available - assert gInterpreter + assert gCling diff --git a/bindings/pyroot/cppyy/cppyy/test/test_crossinheritance.py b/bindings/pyroot/cppyy/cppyy/test/test_crossinheritance.py index c9f8128b2e844..3a1bd5cd79a2b 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_crossinheritance.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_crossinheritance.py @@ -1200,7 +1200,7 @@ def test27_interfaces(self): import cppyy - cppyy.gbl.gInterpreter.Declare("""\ + cppyy.gbl.gCling.Declare("""\ namespace NonStandardOffset { struct Calc1 { virtual int calc1() = 0; diff --git a/bindings/pyroot/cppyy/cppyy/test/test_datatypes.py b/bindings/pyroot/cppyy/cppyy/test/test_datatypes.py index 0de815e89eeca..8057daf9cc791 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_datatypes.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_datatypes.py @@ -14,7 +14,7 @@ def setup_class(cls): cls.datatypes = cppyy.load_reflection_info(cls.test_dct) cls.N = cppyy.gbl.N - at_least_17 = 201402 < cppyy.gbl.gInterpreter.ProcessLine("__cplusplus;") + at_least_17 = 201402 < cppyy.gbl.gCling.ProcessLine("__cplusplus;") cls.has_byte = at_least_17 cls.has_optional = at_least_17 diff --git a/bindings/pyroot/cppyy/cppyy/test/test_fragile.py b/bindings/pyroot/cppyy/cppyy/test/test_fragile.py index c4108258b2b9f..9bb9fe170fff8 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_fragile.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_fragile.py @@ -789,7 +789,7 @@ def test01_abortive_signals(self): class TestSTDNOTINGLOBAL: def setup_class(cls): import cppyy - cls.has_byte = 201402 < cppyy.gbl.gInterpreter.ProcessLine("__cplusplus;") + cls.has_byte = 201402 < cppyy.gbl.gCling.ProcessLine("__cplusplus;") @mark.xfail() def test01_stl_in_std(self): diff --git a/bindings/pyroot/cppyy/cppyy/test/test_lowlevel.py b/bindings/pyroot/cppyy/cppyy/test/test_lowlevel.py index da15f701de84c..099e64511a7d2 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_lowlevel.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_lowlevel.py @@ -15,7 +15,7 @@ def setup_class(cls): cls.datatypes = cppyy.load_reflection_info(cls.test_dct) cls.N = cppyy.gbl.N - at_least_17 = 201402 < cppyy.gbl.gInterpreter.ProcessLine("__cplusplus;") + at_least_17 = 201402 < cppyy.gbl.gCling.ProcessLine("__cplusplus;") cls.has_nested_namespace = at_least_17 def test00_import_all(self): diff --git a/bindings/pyroot/cppyy/cppyy/test/test_regression.py b/bindings/pyroot/cppyy/cppyy/test/test_regression.py index d5fc197536601..1e9ea5a48de52 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_regression.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_regression.py @@ -56,11 +56,11 @@ def test02_dir(self): import cppyy, pydoc - assert not '__abstractmethods__' in dir(cppyy.gbl.gInterpreter) - assert '__class__' in dir(cppyy.gbl.gInterpreter) + assert not '__abstractmethods__' in dir(cppyy.gbl.gCling) + assert '__class__' in dir(cppyy.gbl.gCling) self.__class__.helpout = [] - pydoc.doc(cppyy.gbl.gInterpreter) + pydoc.doc(cppyy.gbl.gCling) helptext = ''.join(self.__class__.helpout) assert 'TInterpreter' in helptext assert 'CPPInstance' in helptext @@ -1020,7 +1020,7 @@ def test35_filesytem(self): import cppyy - if cppyy.gbl.gInterpreter.ProcessLine("__cplusplus;") > 201402: + if cppyy.gbl.gCling.ProcessLine("__cplusplus;") > 201402: cppyy.cppdef("""\ #include std::string stack_std_path() { diff --git a/bindings/pyroot/cppyy/cppyy/test/test_stltypes.py b/bindings/pyroot/cppyy/cppyy/test/test_stltypes.py index c377df50e4f91..6177eafc7f03d 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_stltypes.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_stltypes.py @@ -1697,7 +1697,7 @@ def test01_string_through_string_view(self): """Usage of std::string_view as formal argument""" import cppyy - if cppyy.gbl.gInterpreter.ProcessLine("__cplusplus;") <= 201402: + if cppyy.gbl.gCling.ProcessLine("__cplusplus;") <= 201402: # string_view exists as of C++17 return @@ -1718,7 +1718,7 @@ def test02_string_view_from_unicode(self): """Life-time management of converted unicode strings""" import cppyy, gc - if cppyy.gbl.gInterpreter.ProcessLine("__cplusplus;") <= 201402: + if cppyy.gbl.gCling.ProcessLine("__cplusplus;") <= 201402: # string_view exists as of C++17 return diff --git a/bindings/pyroot/cppyy/cppyy/test/test_templates.py b/bindings/pyroot/cppyy/cppyy/test/test_templates.py index 429705e914594..6c29d268537de 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_templates.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_templates.py @@ -13,7 +13,7 @@ def setup_class(cls): import cppyy cls.templates = cppyy.load_reflection_info(cls.test_dct) - at_least_17 = 201402 < cppyy.gbl.gInterpreter.ProcessLine("__cplusplus;") + at_least_17 = 201402 < cppyy.gbl.gCling.ProcessLine("__cplusplus;") cls.has_integral_v = at_least_17 cls.has_disjunction_v = at_least_17 cls.has_pack_fold = at_least_17 @@ -299,7 +299,7 @@ def test12_template_aliases(self): assert iavec[5] == 5 # with variadic template - if cppyy.gbl.gInterpreter.ProcessLine("__cplusplus;") > 201402: + if cppyy.gbl.gCling.ProcessLine("__cplusplus;") > 201402: assert nsup.matryoshka[int, 3].type assert nsup.matryoshka[int, 3, 4].type assert nsup.make_vector[int , 3] @@ -307,8 +307,8 @@ def test12_template_aliases(self): assert nsup.make_vector[int , 4]().m_val == 4 # with inner types using - if cppyy.gbl.gInterpreter.ProcessLine("__cplusplus;") > 201402: - assert cppyy.gbl.gInterpreter.CheckClassTemplate("using_problem::Bar::Foo") + if cppyy.gbl.gCling.ProcessLine("__cplusplus;") > 201402: + assert cppyy.gbl.gCling.CheckClassTemplate("using_problem::Bar::Foo") assert nsup.Foo assert nsup.Bar.Foo # used to fail diff --git a/bindings/pyroot/pythonizations/python/ROOT/_application.py b/bindings/pyroot/pythonizations/python/ROOT/_application.py index e0668863a4fc2..0a83eff365e95 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_application.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_application.py @@ -11,7 +11,7 @@ import sys import time -from cppyy.gbl import gSystem, gInterpreter, gEnv +from cppyy.gbl import gSystem, gCling, gEnv from ROOT.libROOTPythonizations import InitApplication, InstallGUIEventInputHook @@ -69,7 +69,7 @@ def displayhook(v): # in an interactive Python session. # Therefore, this function will call EndOfLineAction after each interactive # command (to update display etc.) - gInterpreter.EndOfLineAction() + gCling.EndOfLineAction() return orig_dhook(v) sys.displayhook = displayhook diff --git a/bindings/pyroot/pythonizations/python/ROOT/_numbadeclare.py b/bindings/pyroot/pythonizations/python/ROOT/_numbadeclare.py index 5e033d3b77f9f..385951801bc58 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_numbadeclare.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_numbadeclare.py @@ -507,7 +507,7 @@ def pywrapper({SIGNATURE}): ) # Jit wrapper C++ code - err = gbl_namespace.gInterpreter.Declare(cppwrappercode) + err = gbl_namespace.gCling.Declare(cppwrappercode) if not err: raise Exception("Failed to jit C++ wrapper code with cling:\n{}".format(cppwrappercode)) func.__cpp_wrapper__ = cppwrappercode diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rdataframe.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rdataframe.py index f9bb2771a73a8..da800e8a2ccd0 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rdataframe.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rdataframe.py @@ -34,7 +34,7 @@ ~~~{.py} # JIT a C++ function from Python -ROOT.gInterpreter.Declare(""" +ROOT.gCling.Declare(""" bool myFilter(float x) { return x > 10; } @@ -51,7 +51,7 @@ ~~~{.py} ROOT.gSystem.Load("path/to/myLibrary.so") # Library with the myFilter function -ROOT.gInterpreter.Declare('#include "myLibrary.h"') # Header with the declaration of the myFilter function +ROOT.gCling.Declare('#include "myLibrary.h"') # Header with the declaration of the myFilter function df = ROOT.RDataFrame("myTree", "myFile.root") sum = df.Filter("myFilter(x)").Sum("y") print(sum.GetValue()) @@ -208,7 +208,7 @@ def pypowarray(numpyvec, pow): The ROOT::RDF::AsRNode function casts an RDataFrame node to the generic ROOT::RDF::RNode type. From Python, it can be used to pass any RDataFrame node as an argument of a C++ function, as shown below: ~~~{.py} -ROOT.gInterpreter.Declare(""" +ROOT.gCling.Declare(""" ROOT::RDF::RNode MyTransformation(ROOT::RDF::RNode df) { auto myFunc = [](float x){ return -x;}; return df.Define("y", myFunc, {"x"}); @@ -578,7 +578,7 @@ def _ensure_deleter_declared(): except AttributeError: pass - ROOT.gInterpreter.Declare( + ROOT.gCling.Declare( r""" #include diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_ttree.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_ttree.py index 23dbecdc8b15a..c5cec49daae75 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_ttree.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_ttree.py @@ -76,7 +76,7 @@ # Struct as leaflist. This is interpreted on the fly, # but could be known to ROOT by other means, such as # header inclusion or dictionary load. - ROOT.gInterpreter.Declare(''' + ROOT.gCling.Declare(''' struct MyStruct { int myint; float myfloat; @@ -144,7 +144,7 @@ # Struct as leaflist. This is interpreted on the fly, # but could be known to ROOT by other means, such as # header inclusion or dictionary load. - ROOT.gInterpreter.Declare(''' + ROOT.gCling.Declare(''' struct MyStruct { int myint; float myfloat; diff --git a/bindings/pyroot/pythonizations/test/pythonization_decorator.py b/bindings/pyroot/pythonizations/test/pythonization_decorator.py index 78172fb68b8d6..678944de0d850 100644 --- a/bindings/pyroot/pythonizations/test/pythonization_decorator.py +++ b/bindings/pyroot/pythonizations/test/pythonization_decorator.py @@ -19,9 +19,9 @@ class PythonizationDecorator(unittest.TestCase): # Helpers def _define_class(self, class_name, namespace=None): if namespace is None: - ROOT.gInterpreter.ProcessLine('class {cn} {{ }};'.format(cn=class_name)) + ROOT.gCling.ProcessLine('class {cn} {{ }};'.format(cn=class_name)) else: - ROOT.gInterpreter.ProcessLine(''' + ROOT.gCling.ProcessLine(''' namespace {ns} {{ class {cn} {{}}; }}'''.format(ns=namespace, cn=class_name)) @@ -466,7 +466,7 @@ def test_instantiated_classes(self): self._define_class(class_name) self._define_class(class_name, ns) class_template = 'ClassTemplate' - ROOT.gInterpreter.ProcessLine('template class {ct} {{ }};' + ROOT.gCling.ProcessLine('template class {ct} {{ }};' .format(ct=class_template)) templ_type = 'int' diff --git a/bindings/pyroot/pythonizations/test/rdataframe_asnumpy.py b/bindings/pyroot/pythonizations/test/rdataframe_asnumpy.py index dd9dd87f64507..cf4156fd7b310 100644 --- a/bindings/pyroot/pythonizations/test/rdataframe_asnumpy.py +++ b/bindings/pyroot/pythonizations/test/rdataframe_asnumpy.py @@ -99,7 +99,7 @@ def test_read_array(self): """ Testing reading a std::array """ - ROOT.gInterpreter.Declare(""" + ROOT.gCling.Declare(""" std::array create_array(unsigned int n) { return std::array({n, n, n}); } @@ -114,7 +114,7 @@ def test_read_th1f(self): """ Testing reading a TH1F """ - ROOT.gInterpreter.Declare(""" + ROOT.gCling.Declare(""" TH1F create_histo(unsigned int n) { const auto str = TString::Format("h%i", n); return TH1F(str, str, 4, 0, 1); @@ -129,7 +129,7 @@ def test_read_vector_constantsize(self): """ Testing reading a std::vector with constant size """ - ROOT.gInterpreter.Declare(""" + ROOT.gCling.Declare(""" std::vector create_vector_constantsize(unsigned int n) { return std::vector({n, n, n}); } @@ -144,7 +144,7 @@ def test_read_vector_variablesize(self): """ Testing reading a std::vector with variable size """ - ROOT.gInterpreter.Declare(""" + ROOT.gCling.Declare(""" std::vector create_vector_variablesize(unsigned int n) { return std::vector(n); } @@ -161,10 +161,10 @@ def test_read_tlorentzvector(self): """ # The global module index does not have it preloaded and - # gInterpreter.Declare is not allowed to load libPhysics for + # gCling.Declare is not allowed to load libPhysics for # TLorentzVector. Preload the library now. ROOT.gSystem.Load("libPhysics") - ROOT.gInterpreter.Declare(""" + ROOT.gCling.Declare(""" TLorentzVector create_tlorentzvector() { auto v = TLorentzVector(); v.SetPtEtaPhiM(1, 2, 3, 4); @@ -180,7 +180,7 @@ def test_read_custom_class(self): """ Testing reading a custom class injected in the interpreter """ - ROOT.gInterpreter.Declare(""" + ROOT.gCling.Declare(""" struct CustomClass { unsigned int fMember = 42; }; diff --git a/bindings/pyroot/pythonizations/test/rdf_define_pyz.py b/bindings/pyroot/pythonizations/test/rdf_define_pyz.py index 0a45b47df71dc..0613b4150670b 100644 --- a/bindings/pyroot/pythonizations/test/rdf_define_pyz.py +++ b/bindings/pyroot/pythonizations/test/rdf_define_pyz.py @@ -89,7 +89,7 @@ def test_cpp_functor(self): Define operation. """ - ROOT.gInterpreter.Declare(""" + ROOT.gCling.Declare(""" struct MyFunctor { ULong64_t operator()(ULong64_t l) { return l*l; }; @@ -109,7 +109,7 @@ def test_std_function(self): Define operation. """ - ROOT.gInterpreter.Declare(""" + ROOT.gCling.Declare(""" std::function myfun = [](ULong64_t l) { return l*l; }; """) @@ -185,7 +185,7 @@ def test_cpp_free_function(self): for case in test_cases: with self.subTest(case=case["name"]): - ROOT.gInterpreter.Declare(case["decl"]) + ROOT.gCling.Declare(case["decl"]) rdf = ROOT.RDataFrame(5) if "setup_columns" in case: @@ -206,7 +206,7 @@ def test_cpp_free_function_overload(self): Define operation with overloads. """ - ROOT.gInterpreter.Declare(""" + ROOT.gCling.Declare(""" ULong64_t my_free_function_overload(ULong64_t l) { return l; } ULong64_t my_free_function_overload(ULong64_t l, ULong64_t m) { return l * m; } """) @@ -227,7 +227,7 @@ def test_cpp_free_function_template(self): Define operation. """ - ROOT.gInterpreter.Declare(""" + ROOT.gCling.Declare(""" template T my_free_function_template(T l) { return l; } """) diff --git a/bindings/pyroot/pythonizations/test/rdf_filter_pyz.py b/bindings/pyroot/pythonizations/test/rdf_filter_pyz.py index 66106a12f1dfc..3131a33dc9ad3 100755 --- a/bindings/pyroot/pythonizations/test/rdf_filter_pyz.py +++ b/bindings/pyroot/pythonizations/test/rdf_filter_pyz.py @@ -107,7 +107,7 @@ def test_cpp_functor(self): Filter operation. """ - ROOT.gInterpreter.Declare( + ROOT.gCling.Declare( """ struct MyFunctor { @@ -128,7 +128,7 @@ def test_std_function(self): Filter operation. """ - ROOT.gInterpreter.Declare( + ROOT.gCling.Declare( """ std::function myfun = [](ULong64_t l) { return l == 0; }; """ @@ -145,7 +145,7 @@ def test_cpp_free_function(self): Filter operation. """ - ROOT.gInterpreter.Declare( + ROOT.gCling.Declare( """ bool myfun(ULong64_t l) { return l == 0; } """ @@ -162,7 +162,7 @@ def test_cpp_free_function_overload(self): Filter operation with overloads. """ - ROOT.gInterpreter.Declare( + ROOT.gCling.Declare( """ bool myfun(ULong64_t l) { return l == 0; } bool myfun(int l) { return true; } @@ -180,7 +180,7 @@ def test_cpp_free_function_template(self): Filter operation. """ - ROOT.gInterpreter.Declare( + ROOT.gCling.Declare( """ template bool myfun_t(T l) { return l == 0; } diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index a4dd7793f4a65..26587632965f3 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -115,7 +115,7 @@ clang/LLVM technology. #include "cling/Utils/SourceNormalization.h" #include "cling/Interpreter/Exception.h" -#include +#include #include "llvm/IR/GlobalValue.h" #include "llvm/IR/Module.h" diff --git a/interpreter/CppInterOp/include/CppInterOp/CppInterOpDispatch.h b/interpreter/CppInterOp/include/CppInterOp/CppInterOpDispatch.h new file mode 100644 index 0000000000000..0e0a36aef69f6 --- /dev/null +++ b/interpreter/CppInterOp/include/CppInterOp/CppInterOpDispatch.h @@ -0,0 +1,463 @@ +// #include "clang-c/ExternC.h" +// #include "clang-c/Platform.h" +// CppInterOpDispatch.h +#pragma once + +#include +#include +#include +#include + +// LLVM_CLANG_C_EXTERN_C_BEGIN + +typedef void (*__CPP_FUNC)(void); + +struct name_address_pair { + const char *name; + __CPP_FUNC address; +}; + +// returns a pointer to the function with the given name, or nullptr if not found. +extern "C" __attribute__((visibility("default"))) void (*CppGetProcAddress(const unsigned char *procname))(void); + +// LLVM_CLANG_C_EXTERN_C_END + +#define FOR_EACH_CPP_FUNCTION(DO) \ + DO(CreateInterpreter); \ + DO(GetInterpreter); \ + DO(Process); \ + DO(GetResourceDir); \ + DO(AddIncludePath); \ + DO(LoadLibrary); \ + DO(Declare); \ + DO(DeleteInterpreter); \ + DO(IsNamespace); \ + DO(ObjToString); \ + DO(GetQualifiedCompleteName);\ + DO(IsLValueReferenceType); \ + DO(GetNonReferenceType); \ + DO(IsEnumType); \ + DO(GetIntegerTypeFromEnumType);\ + DO(GetReferencedType); \ + DO(IsPointerType); \ + DO(GetPointeeType); \ + DO(GetPointerType); \ + DO(IsReferenceType); \ + DO(GetTypeAsString); \ + DO(GetCanonicalType); \ + DO(HasTypeQualifier); \ + DO(RemoveTypeQualifier); \ + DO(GetUnderlyingType); \ + DO(IsRecordType); \ + DO(IsFunctionPointerType); \ + DO(GetVariableType); \ + DO(GetNamed); \ + DO(GetScopeFromType); \ + DO(GetClassTemplateInstantiationArgs);\ + DO(IsClass); \ + DO(GetType); \ + DO(GetTypeFromScope); \ + DO(GetComplexType); \ + DO(GetIntegerTypeFromEnumScope);\ + DO(GetUnderlyingScope); \ + DO(GetScope); \ + DO(GetGlobalScope); \ + DO(GetScopeFromCompleteName);\ + DO(InstantiateTemplate); \ + DO(GetParentScope); \ + DO(IsTemplate); \ + DO(IsTemplateSpecialization);\ + DO(IsTypedefed); \ + DO(IsClassPolymorphic); \ + DO(Demangle); \ + DO(SizeOf); \ + DO(GetSizeOfType); \ + DO(IsBuiltin); \ + DO(IsComplete); \ + DO(Allocate); \ + DO(Deallocate); \ + DO(Construct); \ + DO(Destruct); \ + DO(MakeFunctionCallable); \ + DO(GetFunctionAddress); \ + DO(IsAbstract); \ + DO(IsEnumScope); \ + DO(IsEnumConstant); \ + DO(IsAggregate); \ + DO(HasDefaultConstructor); \ + DO(IsVariable); \ + DO(GetAllCppNames); \ + DO(GetUsingNamespaces); \ + DO(GetCompleteName); \ + DO(GetDestructor); \ + DO(IsVirtualMethod); \ + DO(GetNumBases); \ + DO(GetName); \ + DO(GetBaseClass); \ + DO(IsSubclass); \ + DO(GetOperator); \ + DO(GetFunctionReturnType); \ + DO(GetBaseClassOffset); \ + DO(GetClassMethods); \ + DO(GetFunctionsUsingName); \ + DO(GetFunctionNumArgs); \ + DO(GetFunctionRequiredArgs);\ + DO(GetFunctionArgName); \ + DO(GetFunctionArgType); \ + DO(GetFunctionArgDefault); \ + DO(GetFunctionPrototype); \ + DO(IsConstMethod); \ + DO(GetFunctionTemplatedDecls);\ + DO(ExistsFunctionTemplate); \ + DO(IsTemplatedFunction); \ + DO(IsStaticMethod); \ + DO(GetClassTemplatedMethods);\ + DO(BestOverloadFunctionMatch);\ + DO(GetOperatorFromSpelling);\ + DO(IsFunctionDeleted); \ + DO(IsPublicMethod); \ + DO(IsProtectedMethod); \ + DO(IsPrivateMethod); \ + DO(IsConstructor); \ + DO(IsDestructor); \ + DO(GetDatamembers); \ + DO(GetStaticDatamembers); \ + DO(GetEnumConstantDatamembers);\ + DO(LookupDatamember); \ + DO(IsLambdaClass); \ + DO(GetQualifiedName); \ + DO(GetVariableOffset); \ + DO(IsPublicVariable); \ + DO(IsProtectedVariable); \ + DO(IsPrivateVariable); \ + DO(IsStaticVariable); \ + DO(IsConstVariable); \ + DO(GetDimensions); \ + DO(GetEnumConstants); \ + DO(GetEnumConstantType); \ + DO(GetEnumConstantValue); \ + DO(DumpScope); \ + DO(AddSearchPath); \ + DO(Evaluate); \ + DO(IsDebugOutputEnabled); \ + DO(EnableDebugOutput); \ + +#define EXTERN_CPP_FUNC(func_name) \ + extern CppAPIType::func_name func_name; + +#define LOAD_CPP_FUNCTION(func_name) \ + func_name = reinterpret_cast(dlGetProcAddress(#func_name)); + +namespace CppDispatch { + // Forward all type aliases + using TCppIndex_t = ::Cpp::TCppIndex_t; + using TCppScope_t = ::Cpp::TCppScope_t; + using TCppConstScope_t = ::Cpp::TCppConstScope_t; + using TCppType_t = ::Cpp::TCppType_t; + using TCppFunction_t = ::Cpp::TCppFunction_t; + using TCppConstFunction_t = ::Cpp::TCppConstFunction_t; + using TCppFuncAddr_t = ::Cpp::TCppFuncAddr_t; + using TInterp_t = ::Cpp::TInterp_t; + using TCppObject_t = ::Cpp::TCppObject_t; + + // Forward declare enums + using Operator = ::Cpp::Operator; + using OperatorArity = ::Cpp::OperatorArity; + using QualKind = ::Cpp::QualKind; + + using TemplateArgInfo = ::Cpp::TemplateArgInfo; + + // Forward declare the class + using JitCall = ::Cpp::JitCall; + + +} + +namespace CppAPIType { + +using GetVersion = std::string (*)(); + +using Demangle = std::string (*)(const std::string& mangled_name); + +using EnableDebugOutput = void (*)(bool value); + +using IsDebugOutputEnabled = bool (*)(); + +using IsAggregate = bool (*)(Cpp::TCppScope_t scope); + +using IsNamespace = bool (*)(Cpp::TCppScope_t scope); + +using IsClass = bool (*)(Cpp::TCppScope_t scope); + +using IsFunction = bool (*)(Cpp::TCppScope_t scope); + +using IsFunctionPointerType = bool (*)(Cpp::TCppType_t type); + +using IsClassPolymorphic = bool (*)(Cpp::TCppScope_t klass); + +using IsComplete = bool (*)(Cpp::TCppScope_t scope); + +using SizeOf = size_t (*)(Cpp::TCppScope_t scope); + +using IsBuiltin = bool (*)(Cpp::TCppType_t type); + +using IsTemplate = bool (*)(Cpp::TCppScope_t handle); + +using IsTemplateSpecialization = bool (*)(Cpp::TCppScope_t handle); + +using IsTypedefed = bool (*)(Cpp::TCppScope_t handle); + +using IsAbstract = bool (*)(Cpp::TCppType_t klass); + +using IsEnumScope = bool (*)(Cpp::TCppScope_t handle); + +using IsEnumConstant = bool (*)(Cpp::TCppScope_t handle); + +using IsEnumType = bool (*)(Cpp::TCppType_t type); + +using HasTypeQualifier = bool (*)(Cpp::TCppType_t type, Cpp::QualKind qual); + +using RemoveTypeQualifier = Cpp::TCppType_t (*)(Cpp::TCppType_t type, Cpp::QualKind qual); + +using AddTypeQualifier = Cpp::TCppType_t (*)(Cpp::TCppType_t type, Cpp::QualKind qual); + +using GetEnums = void (*)(Cpp::TCppScope_t scope, std::vector& Result); + +using IsSmartPtrType = bool (*)(Cpp::TCppType_t type); + +using GetIntegerTypeFromEnumScope = Cpp::TCppType_t (*)(Cpp::TCppScope_t handle); + +using GetIntegerTypeFromEnumType = Cpp::TCppType_t (*)(Cpp::TCppType_t handle); + +using GetEnumConstants = std::vector (*)(Cpp::TCppScope_t scope); + +using GetEnumConstantType = Cpp::TCppType_t (*)(Cpp::TCppScope_t scope); + +using GetEnumConstantValue = Cpp::TCppIndex_t (*)(Cpp::TCppScope_t scope); + +using GetSizeOfType = size_t (*)(Cpp::TCppType_t type); + +using IsVariable = bool (*)(Cpp::TCppScope_t scope); + +using GetName = std::string (*)(Cpp::TCppScope_t klass); + +using GetCompleteName = std::string (*)(Cpp::TCppScope_t klass); + +using GetQualifiedName = std::string (*)(Cpp::TCppScope_t klass); + +using GetQualifiedCompleteName = std::string (*)(Cpp::TCppScope_t klass); + +using GetUsingNamespaces = std::vector (*)(Cpp::TCppScope_t scope); + +using GetGlobalScope = Cpp::TCppScope_t (*)(); + +using GetUnderlyingScope = Cpp::TCppScope_t (*)(Cpp::TCppScope_t scope); + +using GetScope = Cpp::TCppScope_t (*)(const std::string& name, Cpp::TCppScope_t parent); + +using GetScopeFromCompleteName = Cpp::TCppScope_t (*)(const std::string& name); + +using GetNamed = Cpp::TCppScope_t (*)(const std::string& name, Cpp::TCppScope_t parent); + +using GetParentScope = Cpp::TCppScope_t (*)(Cpp::TCppScope_t scope); + +using GetScopeFromType = Cpp::TCppScope_t (*)(Cpp::TCppType_t type); + +using GetNumBases = Cpp::TCppIndex_t (*)(Cpp::TCppScope_t klass); + +using GetBaseClass = Cpp::TCppScope_t (*)(Cpp::TCppScope_t klass, Cpp::TCppIndex_t ibase); + +using IsSubclass = bool (*)(Cpp::TCppScope_t derived, Cpp::TCppScope_t base); + +using GetBaseClassOffset = int64_t (*)(Cpp::TCppScope_t derived, Cpp::TCppScope_t base); + +using GetClassMethods = void (*)(Cpp::TCppScope_t klass, std::vector& methods); + +using GetFunctionTemplatedDecls = void (*)(Cpp::TCppScope_t klass, std::vector& methods); + +using HasDefaultConstructor = bool (*)(Cpp::TCppScope_t scope); + +using GetDefaultConstructor = Cpp::TCppFunction_t (*)(Cpp::TCppScope_t scope); + +using GetDestructor = Cpp::TCppFunction_t (*)(Cpp::TCppScope_t scope); + +using GetFunctionsUsingName = std::vector (*)(Cpp::TCppScope_t scope, const std::string& name); + +using GetFunctionReturnType = Cpp::TCppType_t (*)(Cpp::TCppFunction_t func); + +using GetFunctionNumArgs = Cpp::TCppIndex_t (*)(Cpp::TCppFunction_t func); + +using GetFunctionRequiredArgs = Cpp::TCppIndex_t (*)(Cpp::TCppConstFunction_t func); + +using GetFunctionArgType = Cpp::TCppType_t (*)(Cpp::TCppFunction_t func, Cpp::TCppIndex_t iarg); + +using GetFunctionSignature = std::string (*)(Cpp::TCppFunction_t func); + +using IsFunctionDeleted = bool (*)(Cpp::TCppConstFunction_t function); + +using IsTemplatedFunction = bool (*)(Cpp::TCppFunction_t func); + +using ExistsFunctionTemplate = bool (*)(const std::string& name, Cpp::TCppScope_t parent); + +using GetClassTemplatedMethods = bool (*)(const std::string& name, Cpp::TCppScope_t parent, std::vector& funcs); + +using IsMethod = bool (*)(Cpp::TCppConstFunction_t method); + +using IsPublicMethod = bool (*)(Cpp::TCppFunction_t method); + +using IsProtectedMethod = bool (*)(Cpp::TCppFunction_t method); + +using IsPrivateMethod = bool (*)(Cpp::TCppFunction_t method); + +using IsConstructor = bool (*)(Cpp::TCppConstFunction_t method); + +using IsDestructor = bool (*)(Cpp::TCppConstFunction_t method); + +using IsStaticMethod = bool (*)(Cpp::TCppConstFunction_t method); + +using GetFunctionAddressFromName = Cpp::TCppFuncAddr_t (*)(const char* mangled_name); + +using GetFunctionAddressFromMethod = Cpp::TCppFuncAddr_t (*)(Cpp::TCppFunction_t method); + +using IsVirtualMethod = bool (*)(Cpp::TCppFunction_t method); + +using GetDatamembers = void (*)(Cpp::TCppScope_t scope, std::vector& datamembers); + +using GetStaticDatamembers = void (*)(Cpp::TCppScope_t scope, std::vector& datamembers); + +using GetEnumConstantDatamembers = void (*)(Cpp::TCppScope_t scope, std::vector& datamembers, bool include_enum_class); + +using LookupDatamember = Cpp::TCppScope_t (*)(const std::string& name, Cpp::TCppScope_t parent); + +using IsLambdaClass = bool (*)(Cpp::TCppType_t type); + +using GetVariableType = Cpp::TCppType_t (*)(Cpp::TCppScope_t var); + +using GetVariableOffset = intptr_t (*)(Cpp::TCppScope_t var, Cpp::TCppScope_t parent); + +using IsPublicVariable = bool (*)(Cpp::TCppScope_t var); + +using IsProtectedVariable = bool (*)(Cpp::TCppScope_t var); + +using IsPrivateVariable = bool (*)(Cpp::TCppScope_t var); + +using IsStaticVariable = bool (*)(Cpp::TCppScope_t var); + +using IsConstVariable = bool (*)(Cpp::TCppScope_t var); + +using IsRecordType = bool (*)(Cpp::TCppType_t type); + +using IsPODType = bool (*)(Cpp::TCppType_t type); + +using IsPointerType = bool (*)(Cpp::TCppType_t type); + +using GetPointeeType = Cpp::TCppType_t (*)(Cpp::TCppType_t type); + +using IsReferenceType = bool (*)(Cpp::TCppType_t type); + +using IsLValueReferenceType = bool (*)(Cpp::TCppType_t type); + +using IsRValueReferenceType = bool (*)(Cpp::TCppType_t type); + +using GetPointerType = Cpp::TCppType_t (*)(Cpp::TCppType_t type); + +using GetReferencedType = Cpp::TCppType_t (*)(Cpp::TCppType_t type, bool rvalue); + +using GetNonReferenceType = Cpp::TCppType_t (*)(Cpp::TCppType_t type); + +using GetTypeAsString = std::string (*)(Cpp::TCppType_t type); + +using GetCanonicalType = Cpp::TCppType_t (*)(Cpp::TCppType_t type); + +using JitCallMakeFunctionCallable = Cpp::JitCall (*)(Cpp::TCppConstFunction_t func); + +using IsConstMethod = bool (*)(Cpp::TCppFunction_t method); + +using GetFunctionArgDefault = std::string (*)(Cpp::TCppFunction_t func, Cpp::TCppIndex_t param_index); + +using GetFunctionArgName = std::string (*)(Cpp::TCppFunction_t func, Cpp::TCppIndex_t param_index); + +using GetSpellingFromOperator = std::string (*)(Cpp::Operator op); + +using GetOperatorFromSpelling = Cpp::Operator (*)(const std::string& op); + +using GetOperatorArity = Cpp::OperatorArity (*)(Cpp::TCppFunction_t op); + +using GetOperator = void (*)(Cpp::TCppScope_t scope, Cpp::Operator op, std::vector& operators, Cpp::OperatorArity kind); + +using CreateInterpreter = Cpp::TInterp_t (*)(const std::vector& Args, const std::vector& GpuArgs); + +using DeleteInterpreter = bool (*)(Cpp::TInterp_t interp); + +using ActivateInterpreter = bool (*)(Cpp::TInterp_t interp); + +using GetInterpreter = Cpp::TInterp_t (*)(); + +using UseExternalInterpreter = void (*)(Cpp::TInterp_t interp); + +using AddSearchPath = void (*)(const char* dir, bool isUser, bool prepend); + +using GetResourceDir = const char* (*)(); + +using DetectResourceDir = std::string (*)(const char* ClangBinaryName); + +using DetectSystemCompilerIncludePaths = void (*)(std::vector& Paths, const char* CompilerName); + +using AddIncludePath = void (*)(const char* dir); + +using GetIncludePaths = void (*)(std::vector& IncludePaths, bool withSystem, bool withFlags); + +using Declare = int (*)(const char* code, bool silent); + +using Process = int (*)(const char* code); + +using Evaluate = intptr_t (*)(const char* code, bool* HadError); + +using LookupLibrary = std::string (*)(const char* lib_name); + +using LoadLibrary = bool (*)(const char* lib_stem, bool lookup); + +using UnloadLibrary = void (*)(const char* lib_stem); + +using SearchLibrariesForSymbol = std::string (*)(const char* mangled_name, bool search_system); + +using InsertOrReplaceJitSymbol = bool (*)(const char* linker_mangled_name, uint64_t address); + +using ObjToString = std::string (*)(const char* type, void* obj); + +using GetUnderlyingType = Cpp::TCppType_t (*)(Cpp::TCppType_t type); + +using BestOverloadFunctionMatch = Cpp::TCppFunction_t(*)(const std::vector& candidates, + const std::vector& explicit_types, + const std::vector& arg_types); +using GetFunctionPrototype = std::string (*)(Cpp::TCppFunction_t func); + +using GetDimensions = std::vector (*)(Cpp::TCppType_t var); + +using DumpScope = void (*)(Cpp::TCppScope_t scope); + +using GetClassTemplateInstantiationArgs = void (*)(Cpp::TCppScope_t klass, std::vector& args); + +using GetAllCppNames = void (*)(Cpp::TCppScope_t scope, std::set& names); + +using Deallocate = void(*)(Cpp::TCppScope_t scope, Cpp::TCppObject_t address, Cpp::TCppIndex_t count); + +using Allocate = Cpp::TCppObject_t(*)(Cpp::TCppScope_t scope, Cpp::TCppIndex_t count); + +using InstantiateTemplate = Cpp::TCppScope_t(*)(Cpp::TCppScope_t tmpl, const Cpp::TemplateArgInfo* template_args, size_t template_args_size, bool instantiate_body); + +using GetComplexType = Cpp::TCppType_t(*)(Cpp::TCppType_t type); + +using GetTypeFromScope = Cpp::TCppType_t(*)(Cpp::TCppScope_t klass); + +using GetType = Cpp::TCppType_t(*)(const std::string& type); + +using Construct = Cpp::TCppObject_t(*)(Cpp::TCppScope_t scope, Cpp::TCppObject_t arena, Cpp::TCppIndex_t count); + +using Destruct = bool(*)(Cpp::TCppObject_t This, Cpp::TCppScope_t scope, bool withFree, Cpp::TCppIndex_t count); + +using MakeFunctionCallable = Cpp::JitCall(*)(Cpp::TCppConstFunction_t func); +using GetFunctionAddress = Cpp::TCppFuncAddr_t(*)(Cpp::TCppConstFunction_t func); +} + +// #endif // CPPINTEROP_DISPATCH_H diff --git a/interpreter/CppInterOp/lib/CppInterOp/CMakeLists.txt b/interpreter/CppInterOp/lib/CppInterOp/CMakeLists.txt index d5670bca170a3..fa5b0572c694b 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/CMakeLists.txt +++ b/interpreter/CppInterOp/lib/CppInterOp/CMakeLists.txt @@ -109,6 +109,7 @@ endif() add_llvm_library(clangCppInterOp BUILDTREE_ONLY DISABLE_LLVM_LINK_LLVM_DYLIB CppInterOp.cpp + CppInterOpDispatch.cpp CXCppInterOp.cpp ${DLM} LINK_LIBS diff --git a/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp b/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp index b6cf555e8d524..756f34853302a 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp +++ b/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp @@ -86,6 +86,9 @@ #include #endif // WIN32 +// extern "C" void *__clang_Interpreter_SetValueWithAlloc(void*, void*, void*); +extern "C" void __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, ...); + namespace Cpp { using namespace clang; @@ -728,13 +731,17 @@ TCppScope_t GetNamed(const std::string& name, auto* D = (clang::Decl*)parent; D = GetUnderlyingScope(D); Within = llvm::dyn_cast(D); + Within->getPrimaryContext()->buildLookup(); + // Within->buildLookup(); } - +#ifdef CPPINTEROP_USE_CLING + cling::Interpreter::PushTransactionRAII RAII(&getInterp()); +#endif auto* ND = Cpp_utils::Lookup::Named(&getSema(), name, Within); + if (ND && ND != (clang::NamedDecl*)-1) { return (TCppScope_t)(ND->getCanonicalDecl()); } - return 0; } @@ -3194,6 +3201,35 @@ static std::string MakeResourcesPath() { } } // namespace +static bool DefineAbsoluteSymbol(const char* linker_mangled_name, + uint64_t address) { + using namespace llvm; + using namespace llvm::orc; + + compat::Interpreter &I = getInterp(); + llvm::orc::LLJIT& Jit = *compat::getExecutionEngine(I); + llvm::orc::ExecutionSession& ES = Jit.getExecutionSession(); + JITDylib& DyLib = *Jit.getProcessSymbolsJITDylib().get(); + + llvm::orc::SymbolMap InjectedSymbols; + auto& DL = compat::getExecutionEngine(I)->getDataLayout(); + char GlobalPrefix = DL.getGlobalPrefix(); + std::string tmp(linker_mangled_name); + if (GlobalPrefix != '\0') { + tmp = std::string(1, GlobalPrefix) + tmp; + } + auto Name = ES.intern(tmp); + InjectedSymbols[Name] = + ExecutorSymbolDef(ExecutorAddr(address), JITSymbolFlags::Exported); + + if (Error Err = DyLib.define(absoluteSymbols(InjectedSymbols))) { + logAllUnhandledErrors(std::move(Err), errs(), + "DefineAbsoluteSymbol error: "); + return true; + } + return false; +} + TInterp_t CreateInterpreter(const std::vector& Args /*={}*/, const std::vector& GpuArgs /*={}*/) { std::string MainExecutableName = sys::fs::getMainExecutable(nullptr, nullptr); @@ -3273,6 +3309,11 @@ TInterp_t CreateInterpreter(const std::vector& Args /*={}*/, sInterpreters->emplace_back(I, /*Owned=*/true); +#ifndef CPPINTEROP_USE_CLING + // DefineAbsoluteSymbol("__clang_Interpreter_SetValueWithAlloc", (uint64_t)&__clang_Interpreter_SetValueWithAlloc); + DefineAbsoluteSymbol("__clang_Interpreter_SetValueNoAlloc", (uint64_t)&__clang_Interpreter_SetValueNoAlloc); + // Cpp::DefineAbsoluteSymbol("__clang_Interpreter_NewTag", (uint64_t)&__clang_Interpreter_SetValueNoAlloc); +#endif return I; } @@ -3704,6 +3745,9 @@ void GetAllCppNames(TCppScope_t scope, std::set& names) { clang::DeclContext* DC; clang::DeclContext::decl_iterator decl; +#ifdef CPPINTEROP_USE_CLING + cling::Interpreter::PushTransactionRAII RAII(&getInterp()); +#endif if (auto* TD = dyn_cast_or_null(D)) { DC = clang::TagDecl::castToDeclContext(TD); decl = DC->decls_begin(); diff --git a/interpreter/CppInterOp/lib/CppInterOp/CppInterOpDispatch.cpp b/interpreter/CppInterOp/lib/CppInterOp/CppInterOpDispatch.cpp new file mode 100644 index 0000000000000..cd36a5afc8005 --- /dev/null +++ b/interpreter/CppInterOp/lib/CppInterOp/CppInterOpDispatch.cpp @@ -0,0 +1,226 @@ +#include + +static const struct name_address_pair INTEROP_FUNCTIONS[] = { + { "GetInterpreter", (__CPP_FUNC) Cpp::GetInterpreter }, + { "CreateInterpreter", (__CPP_FUNC) Cpp::CreateInterpreter }, + { "Process", (__CPP_FUNC) Cpp::Process }, + { "GetResourceDir", (__CPP_FUNC) Cpp::GetResourceDir }, + { "AddIncludePath", (__CPP_FUNC) Cpp::AddIncludePath }, + { "LoadLibrary", (__CPP_FUNC) Cpp::LoadLibrary }, + { "Declare", (__CPP_FUNC) Cpp::Declare }, + { "DeleteInterpreter", (__CPP_FUNC) Cpp::DeleteInterpreter }, + { "IsNamespace", (__CPP_FUNC) Cpp::IsNamespace }, + { "ObjToString", (__CPP_FUNC) Cpp::ObjToString }, + { "GetQualifiedCompleteName", (__CPP_FUNC) Cpp::GetQualifiedCompleteName }, + { "IsLValueReferenceType", (__CPP_FUNC) Cpp::IsLValueReferenceType }, + { "GetNonReferenceType", (__CPP_FUNC) Cpp::GetNonReferenceType }, + { "IsEnumType", (__CPP_FUNC) Cpp::IsEnumType }, + { "GetIntegerTypeFromEnumType", (__CPP_FUNC) Cpp::GetIntegerTypeFromEnumType }, + { "GetReferencedType", (__CPP_FUNC) Cpp::GetReferencedType }, + { "IsPointerType", (__CPP_FUNC) Cpp::IsPointerType }, + { "GetPointeeType", (__CPP_FUNC) Cpp::GetPointeeType }, + { "GetPointerType", (__CPP_FUNC) Cpp::GetPointerType }, + { "IsReferenceType", (__CPP_FUNC) Cpp::IsReferenceType }, + { "GetTypeAsString", (__CPP_FUNC) Cpp::GetTypeAsString }, + { "GetCanonicalType", (__CPP_FUNC) Cpp::GetCanonicalType }, + { "HasTypeQualifier", (__CPP_FUNC) Cpp::HasTypeQualifier }, + { "RemoveTypeQualifier", (__CPP_FUNC) Cpp::RemoveTypeQualifier }, + { "GetUnderlyingType", (__CPP_FUNC) Cpp::GetUnderlyingType }, + { "IsRecordType", (__CPP_FUNC) Cpp::IsRecordType }, + { "IsFunctionPointerType", (__CPP_FUNC) Cpp::IsFunctionPointerType }, + { "GetVariableType", (__CPP_FUNC) Cpp::GetVariableType }, + { "GetNamed", (__CPP_FUNC) Cpp::GetNamed }, + { "GetScopeFromType", (__CPP_FUNC) Cpp::GetScopeFromType }, + { "GetClassTemplateInstantiationArgs", (__CPP_FUNC) Cpp::GetClassTemplateInstantiationArgs }, + { "IsClass", (__CPP_FUNC) Cpp::IsClass }, + { "GetType", (__CPP_FUNC) Cpp::GetType }, + { "GetTypeFromScope", (__CPP_FUNC) Cpp::GetTypeFromScope }, + { "GetComplexType", (__CPP_FUNC) Cpp::GetComplexType }, + { "GetIntegerTypeFromEnumScope", (__CPP_FUNC) Cpp::GetIntegerTypeFromEnumScope }, + { "GetUnderlyingScope", (__CPP_FUNC) Cpp::GetUnderlyingScope }, + { "GetScope", (__CPP_FUNC) Cpp::GetScope }, + { "GetGlobalScope", (__CPP_FUNC) Cpp::GetGlobalScope }, + { "GetScopeFromCompleteName", (__CPP_FUNC) Cpp::GetScopeFromCompleteName }, + { "InstantiateTemplate", (__CPP_FUNC) Cpp::InstantiateTemplate }, + { "GetParentScope", (__CPP_FUNC) Cpp::GetParentScope }, + { "IsTemplate", (__CPP_FUNC) Cpp::IsTemplate }, + { "IsTemplateSpecialization", (__CPP_FUNC) Cpp::IsTemplateSpecialization }, + { "IsTypedefed", (__CPP_FUNC) Cpp::IsTypedefed }, + { "IsClassPolymorphic", (__CPP_FUNC) Cpp::IsClassPolymorphic }, + { "Demangle", (__CPP_FUNC) Cpp::Demangle }, + { "SizeOf", (__CPP_FUNC) Cpp::SizeOf }, + { "GetSizeOfType", (__CPP_FUNC) Cpp::GetSizeOfType }, + { "IsBuiltin", (__CPP_FUNC) Cpp::IsBuiltin }, + { "IsComplete", (__CPP_FUNC) Cpp::IsComplete }, + { "Allocate", (__CPP_FUNC) Cpp::Allocate }, + { "Deallocate", (__CPP_FUNC) Cpp::Deallocate }, + { "Construct", (__CPP_FUNC) Cpp::Construct }, + { "Destruct", (__CPP_FUNC) Cpp::Destruct }, + { "MakeFunctionCallable", (__CPP_FUNC) static_cast(&Cpp::MakeFunctionCallable) }, + // { "MakeFunctionCallableInterp", (__CPP_FUNC) static_cast(&Cpp::MakeFunctionCallable) }, + { "GetFunctionAddress", (__CPP_FUNC) static_cast(&Cpp::GetFunctionAddress) }, + { "IsAbstract", (__CPP_FUNC) Cpp::IsAbstract }, + { "IsEnumScope", (__CPP_FUNC) Cpp::IsEnumScope }, + { "IsEnumConstant", (__CPP_FUNC) Cpp::IsEnumConstant }, + { "IsAggregate", (__CPP_FUNC) Cpp::IsAggregate }, + { "HasDefaultConstructor", (__CPP_FUNC) Cpp::HasDefaultConstructor }, + { "IsVariable", (__CPP_FUNC) Cpp::IsVariable }, + { "GetAllCppNames", (__CPP_FUNC) Cpp::GetAllCppNames }, + { "GetUsingNamespaces", (__CPP_FUNC) Cpp::GetUsingNamespaces }, + { "GetCompleteName", (__CPP_FUNC) Cpp::GetCompleteName }, + { "GetDestructor", (__CPP_FUNC) Cpp::GetDestructor }, + { "IsVirtualMethod", (__CPP_FUNC) Cpp::IsVirtualMethod }, + { "GetNumBases", (__CPP_FUNC) Cpp::GetNumBases }, + { "GetName", (__CPP_FUNC) Cpp::GetName }, + { "GetBaseClass", (__CPP_FUNC) Cpp::GetBaseClass }, + { "IsSubclass", (__CPP_FUNC) Cpp::IsSubclass }, + { "GetOperator", (__CPP_FUNC) Cpp::GetOperator }, + { "GetFunctionReturnType", (__CPP_FUNC) Cpp::GetFunctionReturnType }, + { "GetBaseClassOffset", (__CPP_FUNC) Cpp::GetBaseClassOffset }, + { "GetClassMethods", (__CPP_FUNC) Cpp::GetClassMethods }, + { "GetFunctionsUsingName", (__CPP_FUNC) Cpp::GetFunctionsUsingName }, + { "GetFunctionNumArgs", (__CPP_FUNC) Cpp::GetFunctionNumArgs }, + { "GetFunctionRequiredArgs", (__CPP_FUNC) Cpp::GetFunctionRequiredArgs }, + { "GetFunctionArgName", (__CPP_FUNC) Cpp::GetFunctionArgName }, + { "GetFunctionArgType", (__CPP_FUNC) Cpp::GetFunctionArgType }, + { "GetFunctionArgDefault", (__CPP_FUNC) Cpp::GetFunctionArgDefault }, + { "IsConstMethod", (__CPP_FUNC) Cpp::IsConstMethod }, + { "GetFunctionTemplatedDecls", (__CPP_FUNC) Cpp::GetFunctionTemplatedDecls }, + { "ExistsFunctionTemplate", (__CPP_FUNC) Cpp::ExistsFunctionTemplate }, + { "IsTemplatedFunction", (__CPP_FUNC) Cpp::IsTemplatedFunction }, + { "IsStaticMethod", (__CPP_FUNC) Cpp::IsStaticMethod }, + { "GetClassTemplatedMethods", (__CPP_FUNC) Cpp::GetClassTemplatedMethods }, + { "BestOverloadFunctionMatch", (__CPP_FUNC) Cpp::BestOverloadFunctionMatch }, + { "GetOperatorFromSpelling", (__CPP_FUNC) Cpp::GetOperatorFromSpelling }, + { "IsFunctionDeleted", (__CPP_FUNC) Cpp::IsFunctionDeleted }, + { "IsPublicMethod", (__CPP_FUNC) Cpp::IsPublicMethod }, + { "IsProtectedMethod", (__CPP_FUNC) Cpp::IsProtectedMethod }, + { "IsPrivateMethod", (__CPP_FUNC) Cpp::IsPrivateMethod }, + { "IsConstructor", (__CPP_FUNC) Cpp::IsConstructor }, + { "IsDestructor", (__CPP_FUNC) Cpp::IsDestructor }, + { "GetDatamembers", (__CPP_FUNC) Cpp::GetDatamembers }, + { "GetStaticDatamembers", (__CPP_FUNC) Cpp::GetStaticDatamembers }, + { "GetEnumConstantDatamembers", (__CPP_FUNC) Cpp::GetEnumConstantDatamembers }, + { "LookupDatamember", (__CPP_FUNC) Cpp::LookupDatamember }, + { "IsLambdaClass", (__CPP_FUNC) Cpp::IsLambdaClass }, + { "GetQualifiedName", (__CPP_FUNC) Cpp::GetQualifiedName }, + { "GetVariableOffset", (__CPP_FUNC) Cpp::GetVariableOffset }, + { "IsPublicVariable", (__CPP_FUNC) Cpp::IsPublicVariable }, + { "IsProtectedVariable", (__CPP_FUNC) Cpp::IsProtectedVariable }, + { "IsPrivateVariable", (__CPP_FUNC) Cpp::IsPrivateVariable }, + { "IsStaticVariable", (__CPP_FUNC) Cpp::IsStaticVariable }, + { "IsConstVariable", (__CPP_FUNC) Cpp::IsConstVariable }, + { "GetDimensions", (__CPP_FUNC) Cpp::GetDimensions }, + { "GetEnumConstants", (__CPP_FUNC) Cpp::GetEnumConstants }, + { "GetEnumConstantType", (__CPP_FUNC) Cpp::GetEnumConstantType }, + { "GetEnumConstantValue", (__CPP_FUNC) Cpp::GetEnumConstantValue }, + { "DumpScope", (__CPP_FUNC) Cpp::DumpScope }, + { "AddSearchPath", (__CPP_FUNC) Cpp::AddSearchPath }, + { "Evaluate", (__CPP_FUNC) Cpp::Evaluate }, + { "IsDebugOutputEnabled", (__CPP_FUNC) Cpp::IsDebugOutputEnabled }, + { "EnableDebugOutput", (__CPP_FUNC) Cpp::EnableDebugOutput }, + { NULL, NULL } /* end of list */ +}; + +// basic approach: linear search through the static table +// static __CPP_FUNC +// _cppinterop_get_proc_address(const char *funcName) +// { +// unsigned int i; +// for (i = 0; INTEROP_FUNCTIONS[i].Name; i++) { +// if (strcmp(INTEROP_FUNCTIONS[i].Name, funcName) == 0) +// return INTEROP_FUNCTIONS[i].Address; +// } +// return NULL; +// } + +static __CPP_FUNC +_cppinterop_get_proc_address(const char *funcName) +{ + static const std::unordered_map function_map = [] { + std::unordered_map map; + for (int i = 0; INTEROP_FUNCTIONS[i].name != NULL; ++i) { + map[INTEROP_FUNCTIONS[i].name] = INTEROP_FUNCTIONS[i].address; + } + return map; + }(); + + auto it = function_map.find(funcName); + if (it != function_map.end()) { + return it->second; + } + + return NULL; +} + + +/** + * if the function name isn't found in the name of static functions, + * try generating a new API entrypoint on the fly with ASM. + */ + +// struct cppinterop_stub { +// size_t name_offset; +// int slot; +// }; + +// extern const struct cppinterop_stub cppinterop_public_stubs[]; +// extern const size_t cppinterop_public_stubs_count; + +// int cppinterop_stub_compare(const void *key, const void *elem); + +// static int +// stub_compare(const void *key, const void *elem) +// { +// const char *name = (const char *)key; +// const struct cppinterop_stub *stub = (const struct cppinterop_stub *)elem; + +// return strcmp(name, &public_string_pool[stub->name_offset]); +// } + +// static inline const struct cppinterop_stub * +// cppinterop_get_stub(const char *name) +// { +// return (const struct cppinterop_stub *) +// bsearch(name, +// cppinterop_public_stubs, +// cppinterop_public_stubs_count, +// sizeof(cppinterop_public_stubs[0]), +// cppinterop_stub_compare); +// } + +// static __CPP_FUNC +// entry_get_public(int slot) +// { +// return public_entries[slot]; +// } + +// static inline __CPP_FUNC +// _cppinterop_type2_get_proc_address(const char *funcName) +// { +// const struct cppinterop_stub *stub = cppinterop_get_stub(funcName); +// return stub ? entry_get_public(stub->slot) : NULL; + +/** + * Return address of named InterOp API, or NULL if not found. + */ + +static inline __CPP_FUNC +GetProcAddressARB(const unsigned char *procName) +{ + __CPP_FUNC f = nullptr; + + f = _cppinterop_get_proc_address((const char *)procName); + if (f) + return f; + return NULL; + + // Fallback to generating an API endpoint using stubs + // f = _cppinterop_type2_get_proc_address((const char *)procName); + // return f; +} + +void (*CppGetProcAddress(const unsigned char *procName))(void) +{ + return GetProcAddressARB(procName); +} diff --git a/interpreter/CppInterOp/lib/CppInterOp/CppInterOpInterpreter.h b/interpreter/CppInterOp/lib/CppInterOp/CppInterOpInterpreter.h index d87eb33e15827..06a04da01cba0 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/CppInterOpInterpreter.h +++ b/interpreter/CppInterOp/lib/CppInterOp/CppInterOpInterpreter.h @@ -155,6 +155,7 @@ class Interpreter { llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargets(); llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmParsers(); llvm::InitializeAllAsmPrinters(); std::vector vargs(argv + 1, argv + argc); diff --git a/interpreter/CppInterOp/unittests/CppInterOp/CMakeLists.txt b/interpreter/CppInterOp/unittests/CppInterOp/CMakeLists.txt index 4b4b43bdd2936..30d154a1f5fb6 100644 --- a/interpreter/CppInterOp/unittests/CppInterOp/CMakeLists.txt +++ b/interpreter/CppInterOp/unittests/CppInterOp/CMakeLists.txt @@ -12,14 +12,15 @@ else() endif() add_cppinterop_unittest(CppInterOpTests - EnumReflectionTest.cpp - FunctionReflectionTest.cpp - InterpreterTest.cpp - JitTest.cpp - ScopeReflectionTest.cpp - TypeReflectionTest.cpp + # EnumReflectionTest.cpp + # FunctionReflectionTest.cpp + # InterpreterTest.cpp + # JitTest.cpp + # ScopeReflectionTest.cpp + # TypeReflectionTest.cpp Utils.cpp - VariableReflectionTest.cpp + # VariableReflectionTest.cpp + ExternAPI.cpp ${EXTRA_TEST_SOURCE_FILES} ) diff --git a/interpreter/CppInterOp/unittests/CppInterOp/ExternAPI.cpp b/interpreter/CppInterOp/unittests/CppInterOp/ExternAPI.cpp new file mode 100644 index 0000000000000..7f6eab7cdce83 --- /dev/null +++ b/interpreter/CppInterOp/unittests/CppInterOp/ExternAPI.cpp @@ -0,0 +1,148 @@ +#include "Utils.h" + +#include "CppInterOp/CppInterOpDispatch.h" + +#include "clang/AST/ASTContext.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Sema/Sema.h" + +#include "gtest/gtest.h" +#include +#include + +using namespace TestUtils; +using namespace llvm; +using namespace clang; + + +// TEST(ExternAPITest, FindGetVersion) { + +// void* cppinterop_handle = dlopen("libclangCppInterOp.so", RTLD_LOCAL); +// if (!cppinterop_handle) { /* handle error */ } + +// using GetVersionFunc = std::string (*)(); +// GetVersionFunc GetVersion = (GetVersionFunc)dlsym(cppinterop_handle, "Cpp::GetVersion"); +// if (!GetVersion) { /* handle error */ } + +// } + +// TEST(ExternAPITest, GetVersionSymbolLookup) { +// const char* libPath = "/home/ajomy/cppyy-interop-dev/CppInterOp/build/lib/libclangCppInterOp.so"; +// void* handle = dlopen(libPath, RTLD_LOCAL | RTLD_NOW); +// ASSERT_NE(handle, nullptr) << "Failed to open library: " << dlerror(); + +// typedef const char* (*GetVersionFn)(); +// GetVersionFn get_version = (GetVersionFn)dlsym(handle, "cppinterop_get_version_c"); +// ASSERT_NE(get_version, nullptr) << "failed to locate symbol: " << dlerror(); +// const char* version_cstr = get_version(); +// ASSERT_NE(version_cstr, nullptr) << "string is null"; +// std::string version(version_cstr); +// std::cout<<"\nHELLO\n"< Decls; +// GetAllTopLevelDecls("namespace N {} class C{}; int I;", Decls); +// EXPECT_FALSE(IsClassExposed(Decls[0])); +// EXPECT_TRUE(IsClassExposed(Decls[1])); +// EXPECT_FALSE(IsClassExposed(Decls[2])); + +// dlclose(handle); +// } + + +// void* dlGetProcAddress (const char* name) +// { + +// const char* libPath = "/home/ajomy/cppyy-interop-dev/CppInterOp/build/lib/libclangCppInterOp.so"; +// static void* handle = dlopen(libPath, RTLD_LOCAL | RTLD_NOW); +// if (!handle) return nullptr; +// // ASSERT_NE(handle, nullptr) << "Failed to open library: " << dlerror(); +// static void* gpa = dlsym(handle, "CppGetProcAddress"); +// if (!gpa) return nullptr; +// // ASSERT_NE(gpa, nullptr) << "Failed to find GetProcAddress" << dlerror(); +// dlclose(handle); +// return ((void*(*)(const char*))gpa)(name); +// } + +void* dlGetProcAddress (const char* name) +{ + // const char* libPath = "/home/ajomy/cppyy-interop-dev/CppInterOp/build/lib/libclangCppInterOp.so"; + const char* libPath = "/home/ajomy/ROOT/root_build/lib/libCling.so"; + static void* handle = nullptr; + handle = dlopen(libPath, RTLD_LAZY|RTLD_LOCAL); + if (!handle) + std::cout << "Failed to open li: " << dlerror(); + static void* (*gpa)(const char*) = nullptr; + gpa = reinterpret_cast(dlsym(handle, "CppGetProcAddress")); + std::cout<<"\nGPA: "<(dlGetProcAddress("IsClass")); + ASSERT_NE(IsClassExposed, nullptr) << "failed to locate symbol: " << dlerror(); + std::vector Decls; + GetAllTopLevelDecls("namespace N {} class C{}; int I;", Decls); + EXPECT_FALSE(IsClassExposed(Decls[0])); + EXPECT_TRUE(IsClassExposed(Decls[1])); + EXPECT_FALSE(IsClassExposed(Decls[2])); +} + +TEST(ExternAPITest, Demangle) { + + std::string code = R"( + int add(int x, int y) { return x + y; } + int add(double x, double y) { return x + y; } + )"; + + std::vector Decls; + GetAllTopLevelDecls(code, Decls); + EXPECT_EQ(Decls.size(), 2); + + auto Add_int = clang::GlobalDecl(static_cast(Decls[0])); + auto Add_double = clang::GlobalDecl(static_cast(Decls[1])); + + std::string mangled_add_int; + std::string mangled_add_double; + compat::maybeMangleDeclName(Add_int, mangled_add_int); + compat::maybeMangleDeclName(Add_double, mangled_add_double); + + // using fDemangle = std::string (*)(const std::string scope); + CppAPIType::Demangle DemangleDispatched = reinterpret_cast(dlGetProcAddress("Demangle")); + CppAPIType::GetQualifiedCompleteName GetQualifiedCompleteNameDispatched = reinterpret_cast(dlGetProcAddress("GetQualifiedCompleteName")); + + // using namespace CppDispatch + std::string demangled_add_int = DemangleDispatched(mangled_add_int); + std::string demangled_add_double = DemangleDispatched(mangled_add_double); + + std::cout << "mangled_add_int: " << mangled_add_int << "\n"; + std::cout << "demangled_add_int: " << demangled_add_int << "\n"; + std::cout << "mangled_add_double: " << mangled_add_double << "\n"; + std::cout << "demangled_add_double: " << demangled_add_double << "\n"; + std::cout << "GetQualifiedCompleteNameDispatched(Decls[0]): " << GetQualifiedCompleteNameDispatched(Decls[0]) << "\n"; + std::cout << "GetQualifiedCompleteNameDispatched(Decls[1]): " << GetQualifiedCompleteNameDispatched(Decls[1]) << "\n"; + EXPECT_NE(demangled_add_int.find(GetQualifiedCompleteNameDispatched(Decls[0])), + std::string::npos); + EXPECT_NE(demangled_add_double.find(GetQualifiedCompleteNameDispatched(Decls[1])), + std::string::npos); +}