diff --git a/bindings/pyroot/src/Cppyy.cxx b/bindings/pyroot/src/Cppyy.cxx index 87c18b3a64eb703d28bfc8bd1e790b3bb322be99..37286b1afe9bae9c06b5e639da7fb7e8a1ef02e8 100644 --- a/bindings/pyroot/src/Cppyy.cxx +++ b/bindings/pyroot/src/Cppyy.cxx @@ -538,6 +538,9 @@ size_t Cppyy::GetFunctionArgTypeoffset()\ // scope reflection information ---------------------------------------------- Bool_t Cppyy::IsNamespace( TCppScope_t scope ) { // Test if this scope represents a namespace. + if (scope == GLOBAL_HANDLE) + return kTRUE; + TClassRef& cr = type_from_handle( scope ); if ( cr.GetClass() ) return cr->Property() & kIsNamespace; @@ -816,6 +819,33 @@ Bool_t Cppyy::IsConstMethod( TCppMethod_t method ) } +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()); + } + + return false; +} + +Cppyy::TCppMethod_t Cppyy::GetMethodTemplate( + TCppScope_t scope, const std::string& name, const std::string& proto) +{ + if (scope == (TCppScope_t)GLOBAL_HANDLE) { + return (TCppMethod_t)gROOT->GetGlobalFunctionWithPrototype(name.c_str(), proto.c_str()); + } else { + TClassRef& cr = type_from_handle(scope); + if (cr.GetClass()) + return (TCppMethod_t)cr->GetMethodWithPrototype(name.c_str(), proto.c_str()); + } + + return (TCppMethod_t)nullptr; +} + Bool_t Cppyy::IsMethodTemplate( TCppMethod_t method ) { if ( method ) { diff --git a/bindings/pyroot/src/Cppyy.h b/bindings/pyroot/src/Cppyy.h index e3b331a415c4c4c11a9cbda42bbdba7fbf29d130..67cf01523839fe8b2f136a9ac0f34a5e43d47d86 100644 --- a/bindings/pyroot/src/Cppyy.h +++ b/bindings/pyroot/src/Cppyy.h @@ -99,6 +99,8 @@ namespace Cppyy { std::string GetMethodSignature( TCppScope_t scope, TCppIndex_t imeth ); Bool_t IsConstMethod( TCppMethod_t ); + bool ExistsMethodTemplate(TCppScope_t scope, const std::string& name); + TCppMethod_t GetMethodTemplate(TCppScope_t scope, const std::string& name, const std::string& proto); Bool_t IsMethodTemplate( TCppMethod_t ); TCppIndex_t GetMethodNumTemplateArgs( TCppScope_t scope, TCppIndex_t imeth ); std::string GetMethodTemplateArgName( TCppScope_t scope, TCppIndex_t imeth, TCppIndex_t iarg ); diff --git a/bindings/pyroot/src/RootWrapper.cxx b/bindings/pyroot/src/RootWrapper.cxx index 0e136978ab5f353b89c785e443648d48e175b6ec..d87985ac567d3f466cf4738e17775356d896ac07 100644 --- a/bindings/pyroot/src/RootWrapper.cxx +++ b/bindings/pyroot/src/RootWrapper.cxx @@ -789,6 +789,11 @@ PyObject* PyROOT::GetCppGlobal( const std::string& name ) return (PyObject*)MethodProxy_New( name, overloads ); } + // Try function templates + if (Cppyy::ExistsMethodTemplate(Cppyy::gGlobalScope, name)) { + return (PyObject*)TemplateProxy_New(name, CreateScopeProxy("")); + } + // allow lookup into std as if global (historic) TDataMember* dm = TClass::GetClass( "std" )->GetDataMember( name.c_str() ); if ( dm ) { diff --git a/bindings/pyroot/src/TemplateProxy.cxx b/bindings/pyroot/src/TemplateProxy.cxx index 27773d636c34d055928f90fc9c7c6bd61faace00..5b841a0bc018e81326fde5b1252cc485b46cbc2a 100644 --- a/bindings/pyroot/src/TemplateProxy.cxx +++ b/bindings/pyroot/src/TemplateProxy.cxx @@ -9,6 +9,7 @@ #include "PyCallable.h" #include "PyStrings.h" #include "Utility.h" +#include "PyRootType.h" // ROOT #include "TClass.h" @@ -194,38 +195,6 @@ namespace { // case 2: non-instantiating obj->method< t0, t1, ... >( a0, a1, ... ) - // build "< type, type, ... >" part of method name - PyObject* pyname_v1 = Utility::BuildTemplateName( pytmpl->fPyName, args, 0 ); - if ( pyname_v1 ) { - // lookup method on self (to make sure it propagates), which is readily callable - pymeth = PyObject_GetAttr( pytmpl->fSelf ? pytmpl->fSelf : pytmpl->fPyClass, pyname_v1 ); - if ( pymeth ) { // overloads stop here, as this is an explicit match - Py_DECREF( pyname_v1 ); - return pymeth; // callable method, next step is by user - } - } - PyErr_Clear(); - - // case 3: loop over all previously instantiated templates - pymeth = MethodProxy_Type.tp_descr_get( - (PyObject*)pytmpl->fTemplated, pytmpl->fSelf, (PyObject*)&MethodProxy_Type ); - if ( MethodProxy_Check( pymeth ) ) { - // now call the method with the arguments - PyObject* result = MethodProxy_Type.tp_call( pymeth, args, kwds ); - Py_DECREF( pymeth ); pymeth = 0; - if ( result ) { - Py_XDECREF( pyname_v1 ); - return result; - } - // TODO: collect error here, as the failure may be either an overload - // failure after which we should continue; or a real failure, which should - // be reported. - } - Py_XDECREF( pymeth ); pymeth = 0; - PyErr_Clear(); - - // still here? try instantiating methods - Bool_t isType = kFALSE; Int_t nStrings = 0; PyObject* tpArgs = PyTuple_New( nArgs ); @@ -274,9 +243,44 @@ namespace { Py_XDECREF( pytc ); } + // build "< type, type, ... >" part of method name + PyObject* pyname_v1 = Utility::BuildTemplateName( pytmpl->fPyName, args, 0 ); + if ((isType || nStrings == nArgs) && pyname_v1) { // types in args or all strings + // lookup method on self (to make sure it propagates), which is readily callable + pymeth = PyObject_GetAttr( pytmpl->fSelf ? pytmpl->fSelf : pytmpl->fPyClass, pyname_v1 ); + if ( pymeth ) { // overloads stop here, as this is an explicit match + Py_DECREF( pyname_v1 ); + return pymeth; // callable method, next step is by user + } + } + PyErr_Clear(); + + // case 3: loop over all previously instantiated templates + pymeth = MethodProxy_Type.tp_descr_get( + (PyObject*)pytmpl->fTemplated, pytmpl->fSelf, (PyObject*)&MethodProxy_Type ); + if ( MethodProxy_Check( pymeth ) ) { + // now call the method with the arguments + PyObject* result = MethodProxy_Type.tp_call( pymeth, args, kwds ); + Py_DECREF( pymeth ); pymeth = 0; + if ( result ) { + Py_XDECREF( pyname_v1 ); + return result; + } + // TODO: collect error here, as the failure may be either an overload + // failure after which we should continue; or a real failure, which should + // be reported. + } + Py_XDECREF( pymeth ); pymeth = 0; + PyErr_Clear(); + + // still here? try instantiating methods + PyObject* clName = PyObject_GetAttr( pytmpl->fPyClass, PyStrings::gCppName ); if ( ! clName ) clName = PyObject_GetAttr( pytmpl->fPyClass, PyStrings::gName ); - TClass* klass = TClass::GetClass( PyROOT_PyUnicode_AsString( clName ) ); + auto clNameStr = std::string(PyROOT_PyUnicode_AsString(clName)); + if (clNameStr == "_global_cpp") + clNameStr = ""; // global namespace + TClass* klass = TClass::GetClass(clNameStr.c_str()); Py_DECREF( clName ); const std::string& tmplname = pytmpl->fNonTemplated->fMethodInfo->fName; @@ -288,23 +292,23 @@ namespace { Py_DECREF( pyname_v2 ); std::string proto = mname.substr( 1, mname.size() - 2 ); // the following causes instantiation as necessary - TMethod* cppmeth = klass ? klass->GetMethodWithPrototype( tmplname.c_str(), proto.c_str() ) : 0; + auto scope = Cppyy::GetScope(clNameStr); + auto cppmeth = Cppyy::GetMethodTemplate(scope, tmplname, proto); if ( cppmeth ) { // overload stops here Py_XDECREF( pyname_v1 ); - Cppyy::TCppScope_t scope = Cppyy::GetScope( klass->GetName() ); - if ( (klass->Property() & kIsNamespace) || (cppmeth->Property() & kIsStatic) ) { - pytmpl->fTemplated->AddMethod( new TFunctionHolder( scope, (Cppyy::TCppMethod_t)cppmeth ) ); + if (Cppyy::IsNamespace(scope) || Cppyy::IsStaticMethod(cppmeth)) { + pytmpl->fTemplated->AddMethod( new TFunctionHolder( scope, cppmeth ) ); pymeth = (PyObject*)MethodProxy_New( - cppmeth->GetName(), new TFunctionHolder( scope, (Cppyy::TCppMethod_t)cppmeth ) ); + Cppyy::GetMethodName(cppmeth).c_str(), new TFunctionHolder( scope, cppmeth ) ); } else { - pytmpl->fTemplated->AddMethod( new TMethodHolder( scope, (Cppyy::TCppMethod_t)cppmeth ) ); + pytmpl->fTemplated->AddMethod( new TMethodHolder( scope, cppmeth ) ); pymeth = (PyObject*)MethodProxy_New( - cppmeth->GetName(), new TMethodHolder( scope, (Cppyy::TCppMethod_t)cppmeth ) ); + Cppyy::GetMethodName(cppmeth).c_str(), new TMethodHolder( scope, cppmeth ) ); } - PyObject_SetAttrString( pytmpl->fPyClass, (char*)cppmeth->GetName(), (PyObject*)pymeth ); + PyObject_SetAttrString( pytmpl->fPyClass, (char*)Cppyy::GetMethodName(cppmeth).c_str(), (PyObject*)pymeth ); Py_DECREF( pymeth ); pymeth = PyObject_GetAttrString( - pytmpl->fSelf ? pytmpl->fSelf : pytmpl->fPyClass, (char*)cppmeth->GetName() ); + pytmpl->fSelf ? pytmpl->fSelf : pytmpl->fPyClass, (char*)Cppyy::GetMethodName(cppmeth).c_str() ); PyObject* result = MethodProxy_Type.tp_call( pymeth, args, kwds ); Py_DECREF( pymeth ); return result;