From afd8d310ec5bbb38fd9d86f67821eb12bf5231a4 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Mon, 27 Oct 2025 16:37:13 +0100 Subject: [PATCH 1/7] Add CppInterOp API dispatch mechanism With this patch, we enable clingwrapper to load all of the CppInterOp API required by cppyy using the gInterpreterLib handle used in TROOT This mechanism cannot open libCling unless libCore is loaded. --- core/metacling/src/TCling.cxx | 2 +- .../include/CppInterOp/CppInterOpDispatch.h | 463 ++++++++++++++++++ .../CppInterOp/lib/CppInterOp/CMakeLists.txt | 1 + .../CppInterOp/lib/CppInterOp/CppInterOp.cpp | 37 ++ .../lib/CppInterOp/CppInterOpDispatch.cpp | 226 +++++++++ .../lib/CppInterOp/CppInterOpInterpreter.h | 1 + .../unittests/CppInterOp/CMakeLists.txt | 15 +- .../unittests/CppInterOp/ExternAPI.cpp | 148 ++++++ 8 files changed, 885 insertions(+), 8 deletions(-) create mode 100644 interpreter/CppInterOp/include/CppInterOp/CppInterOpDispatch.h create mode 100644 interpreter/CppInterOp/lib/CppInterOp/CppInterOpDispatch.cpp create mode 100644 interpreter/CppInterOp/unittests/CppInterOp/ExternAPI.cpp 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..9db807167297c 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; @@ -3194,6 +3197,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 +3305,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; } 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); +} From 4564944ea9fda811919e5a8002ccc01335ab6baf Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Mon, 27 Oct 2025 16:37:13 +0100 Subject: [PATCH 2/7] [cppyy] Enable CppInterOp in clingwrapper --- .../pyroot/cppyy/cppyy-backend/CMakeLists.txt | 8 +- .../clingwrapper/src/callcontext.h | 2 +- .../clingwrapper/src/clingwrapper.cxx | 2910 ++++++----------- .../clingwrapper/src/cpp_cppyy.h | 308 +- .../clingwrapper/src/cppinterop_dispatch.cxx | 17 + .../clingwrapper/src/cppinterop_dispatch.h | 56 + 6 files changed, 1330 insertions(+), 1971 deletions(-) create mode 100644 bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cppinterop_dispatch.cxx create mode 100644 bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/cppinterop_dispatch.h 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: "< Date: Mon, 27 Oct 2025 16:37:13 +0100 Subject: [PATCH 3/7] [interop] Add required cling transactions to prevent crashes in GetNamed and GetAllCppNames --- interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp b/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp index 9db807167297c..756f34853302a 100644 --- a/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp +++ b/interpreter/CppInterOp/lib/CppInterOp/CppInterOp.cpp @@ -731,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; } @@ -3741,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(); From 0d5a63c757648105212a2f9003754be6c18cc291 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Mon, 27 Oct 2025 16:37:13 +0100 Subject: [PATCH 4/7] [cppyy] Update CPyCppyy API to use InterOp based forks --- .../cppyy/CPyCppyy/include/CPyCppyy/API.h | 11 +- .../CPyCppyy/include/CPyCppyy/CommonDefs.h | 8 +- bindings/pyroot/cppyy/CPyCppyy/src/API.cxx | 19 +- .../cppyy/CPyCppyy/src/CPPConstructor.cxx | 17 +- .../cppyy/CPyCppyy/src/CPPDataMember.cxx | 96 ++-- .../pyroot/cppyy/CPyCppyy/src/CPPDataMember.h | 7 +- .../pyroot/cppyy/CPyCppyy/src/CPPEnum.cxx | 43 +- bindings/pyroot/cppyy/CPyCppyy/src/CPPEnum.h | 2 + .../pyroot/cppyy/CPyCppyy/src/CPPInstance.cxx | 22 +- .../pyroot/cppyy/CPyCppyy/src/CPPInstance.h | 16 +- .../pyroot/cppyy/CPyCppyy/src/CPPMethod.cxx | 213 +++----- .../pyroot/cppyy/CPyCppyy/src/CPPMethod.h | 2 - .../pyroot/cppyy/CPyCppyy/src/CPPOperator.cxx | 14 +- .../pyroot/cppyy/CPyCppyy/src/CPPOverload.cxx | 121 ++--- .../pyroot/cppyy/CPyCppyy/src/CPPOverload.h | 6 +- .../pyroot/cppyy/CPyCppyy/src/CPPScope.cxx | 78 ++- bindings/pyroot/cppyy/CPyCppyy/src/CPPScope.h | 8 +- .../cppyy/CPyCppyy/src/CPyCppyyModule.cxx | 113 ++-- .../pyroot/cppyy/CPyCppyy/src/CallContext.cxx | 29 + .../pyroot/cppyy/CPyCppyy/src/CallContext.h | 18 + .../pyroot/cppyy/CPyCppyy/src/Converters.cxx | 495 +++++++++++------- .../pyroot/cppyy/CPyCppyy/src/Converters.h | 11 +- bindings/pyroot/cppyy/CPyCppyy/src/Cppyy.h | 178 +++++-- .../cppyy/CPyCppyy/src/CustomPyTypes.cxx | 2 +- .../pyroot/cppyy/CPyCppyy/src/CustomPyTypes.h | 5 - .../cppyy/CPyCppyy/src/DeclareConverters.h | 89 ++-- .../cppyy/CPyCppyy/src/DeclareExecutors.h | 16 +- .../pyroot/cppyy/CPyCppyy/src/DispatchPtr.cxx | 27 +- .../pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx | 89 ++-- .../pyroot/cppyy/CPyCppyy/src/Executors.cxx | 193 +++++-- .../pyroot/cppyy/CPyCppyy/src/Executors.h | 1 + .../cppyy/CPyCppyy/src/LowLevelViews.cxx | 40 -- .../pyroot/cppyy/CPyCppyy/src/LowLevelViews.h | 9 - .../cppyy/CPyCppyy/src/MemoryRegulator.cxx | 20 +- .../cppyy/CPyCppyy/src/ProxyWrappers.cxx | 381 +++++++------- .../pyroot/cppyy/CPyCppyy/src/ProxyWrappers.h | 8 +- .../pyroot/cppyy/CPyCppyy/src/PyCallable.h | 2 - .../pyroot/cppyy/CPyCppyy/src/PyException.cxx | 16 +- .../pyroot/cppyy/CPyCppyy/src/PyStrings.cxx | 3 + .../pyroot/cppyy/CPyCppyy/src/PyStrings.h | 1 + .../pyroot/cppyy/CPyCppyy/src/Pythonize.cxx | 164 +++--- .../pyroot/cppyy/CPyCppyy/src/Pythonize.h | 2 +- .../cppyy/CPyCppyy/src/SignalTryCatch.h | 11 +- .../cppyy/CPyCppyy/src/TemplateProxy.cxx | 52 +- .../cppyy/CPyCppyy/src/TupleOfInstances.cxx | 2 +- .../cppyy/CPyCppyy/src/TupleOfInstances.h | 2 +- .../pyroot/cppyy/CPyCppyy/src/TypeManip.cxx | 12 +- .../pyroot/cppyy/CPyCppyy/src/Utility.cxx | 359 ++++++++++--- bindings/pyroot/cppyy/CPyCppyy/src/Utility.h | 29 +- 49 files changed, 1751 insertions(+), 1311 deletions(-) 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(); From 0eb7287606ffbe12ffee8b19b0b0ffec3359dae5 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Mon, 27 Oct 2025 16:37:13 +0100 Subject: [PATCH 5/7] minimal updates to cppyy frontend to make new cppyy work --- .../cppyy/cppyy/python/cppyy/__init__.py | 176 ++++++++++-------- .../cppyy/python/cppyy/_cpython_cppyy.py | 66 +++---- 2 files changed, 128 insertions(+), 114 deletions(-) diff --git a/bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py b/bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py index b04aeffca000e..ec1b19d1e9d0a 100644 --- a/bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py +++ b/bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py @@ -50,11 +50,21 @@ 'set_debug', # enable/disable debug output ] -import ctypes -import os -import sys -import sysconfig -import warnings +from ._version import __version__ + +import ctypes, os, sys, sysconfig, warnings + +if not 'CLING_STANDARD_PCH' in os.environ: + def _set_pch(): + try: + import cppyy_backend as cpb + local_pch = os.path.join(os.path.dirname(__file__), 'allDict.cxx.pch.'+str(cpb.__version__)) + if os.path.exists(local_pch): + os.putenv('CLING_STANDARD_PCH', local_pch) + os.environ['CLING_STANDARD_PCH'] = local_pch + except (ImportError, AttributeError): + pass + _set_pch(); del _set_pch try: import __pypy__ @@ -63,9 +73,6 @@ except ImportError: ispypy = False -from . import _typemap -from ._version import __version__ - # import separately instead of in the above try/except block for easier to # understand tracebacks if ispypy: @@ -79,7 +86,17 @@ sys.modules['cppyy.gbl.std'] = gbl.std +#- force creation of std.exception ------------------------------------------------------- +_e = gbl.std.exception + + +#- enable auto-loading ------------------------------------------------------- +try: gbl.cling.runtime.gCling.EnableAutoLoading() +except: pass + + #- external typemap ---------------------------------------------------------- +from . import _typemap _typemap.initialize(_backend) # also creates (u)int8_t mapper try: @@ -113,15 +130,18 @@ def tuple_getitem(self, idx, get=cppyy.gbl.std.get): raise IndexError(idx) pyclass.__getitem__ = tuple_getitem - # pythonization of std::string; placed here because it's simpler to write the + # pythonization of std::basic_string; 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/_cpython_cppyy.py b/bindings/pyroot/cppyy/cppyy/python/cppyy/_cpython_cppyy.py index f002931b0e717..5121b861ff0cd 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', @@ -18,25 +16,19 @@ '_end_capture_stderr' ] -# First load the dependency libraries of the backend, then pull in the libcppyy -# extension module. If the backed can't be loaded, it was probably linked -# statically into the extension module, so we don't error out at this point. -try: - from cppyy_backend import loader - c = loader.load_cpp_backend() -except ImportError: - c = None - +# first load the dependency libraries of the backend, then pull in the +# libcppyy extension module +c = loader.load_cpp_backend() import libcppyy as _backend - -if c is not None: - _backend._cpp_backend = c +_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 +60,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 +81,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 +96,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 +115,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 +126,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 +152,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 +191,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() From 1f9de1ce17686c1bc3d8624076dc30da257b34dc Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Mon, 27 Oct 2025 16:37:13 +0100 Subject: [PATCH 6/7] Workaround macro lookup failure with gInterpreter -> gCling in Python --- bindings/distrdf/python/DistRDF/Backends/Base.py | 4 ++-- bindings/distrdf/python/DistRDF/Backends/Utils.py | 6 +++--- bindings/distrdf/test/backend/test_common.py | 2 +- .../cppyy/python/cppyy/__pyinstaller/hook-cppyy.py | 2 +- .../pyroot/cppyy/cppyy/test/assert_interactive.py | 2 +- .../cppyy/cppyy/test/test_crossinheritance.py | 2 +- bindings/pyroot/cppyy/cppyy/test/test_datatypes.py | 2 +- bindings/pyroot/cppyy/cppyy/test/test_fragile.py | 2 +- bindings/pyroot/cppyy/cppyy/test/test_lowlevel.py | 2 +- .../pyroot/cppyy/cppyy/test/test_regression.py | 8 ++++---- bindings/pyroot/cppyy/cppyy/test/test_stltypes.py | 4 ++-- bindings/pyroot/cppyy/cppyy/test/test_templates.py | 8 ++++---- .../pythonizations/python/ROOT/_application.py | 4 ++-- .../pythonizations/python/ROOT/_numbadeclare.py | 2 +- .../python/ROOT/_pythonization/_rdataframe.py | 8 ++++---- .../python/ROOT/_pythonization/_ttree.py | 4 ++-- .../pythonizations/test/pythonization_decorator.py | 6 +++--- .../pythonizations/test/rdataframe_asnumpy.py | 14 +++++++------- .../pyroot/pythonizations/test/rdf_define_pyz.py | 10 +++++----- .../pyroot/pythonizations/test/rdf_filter_pyz.py | 10 +++++----- 20 files changed, 51 insertions(+), 51 deletions(-) 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/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/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; } From 17384f11d3822a55ad5040ed8871fd77b6db7dcb Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Mon, 27 Oct 2025 16:37:13 +0100 Subject: [PATCH 7/7] We do not use shared libs for cppyy_backend, revert loader.py usage --- .../cppyy/cppyy/python/cppyy/_cpython_cppyy.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/bindings/pyroot/cppyy/cppyy/python/cppyy/_cpython_cppyy.py b/bindings/pyroot/cppyy/cppyy/python/cppyy/_cpython_cppyy.py index 5121b861ff0cd..a51feede961c3 100644 --- a/bindings/pyroot/cppyy/cppyy/python/cppyy/_cpython_cppyy.py +++ b/bindings/pyroot/cppyy/cppyy/python/cppyy/_cpython_cppyy.py @@ -16,11 +16,19 @@ '_end_capture_stderr' ] -# first load the dependency libraries of the backend, then pull in the -# libcppyy extension module -c = loader.load_cpp_backend() +# First load the dependency libraries of the backend, then pull in the libcppyy +# extension module. If the backed can't be loaded, it was probably linked +# statically into the extension module, so we don't error out at this point. +try: + from cppyy_backend import loader + c = loader.load_cpp_backend() +except ImportError: + c = None + import libcppyy as _backend -_backend._cpp_backend = c + +if c is not None: + _backend._cpp_backend = c # explicitly expose APIs from libcppyy import ctypes