diff --git a/CMakeLists.txt b/CMakeLists.txt index b63ab18..7bc50bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.16) project(CppJIT LANGUAGES C CXX) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -11,7 +11,7 @@ include(GNUInstallDirs) # This option won't make a lot of sense since we only ship the shared library in site-packages # Perhaps this should permanently be OFF and users can build their own CppInterOp if they want to run the tests? option(CPPJIT_ENABLE_CPPINTEROP_TESTS "enable CppInterOp tests" OFF) -set(CPPINTEROP_GIT_TAG "main" CACHE STRING "") +set(CPPINTEROP_GIT_TAG "a438c13316a8c1917cb86e00d5b0d95e6aee75f5" CACHE STRING "") set(Python_FIND_VIRTUALENV ONLY) find_package(Python COMPONENTS Interpreter Development) @@ -78,41 +78,9 @@ else() set(CPPINTEROP_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/cppjit/cppyy_backend") endif() -set(_interop_cmake_args - -DLLVM_DIR=${LLVM_DIR} - -DCPPINTEROP_USE_REPL=ON - -DCPPINTEROP_USE_CLING=OFF - -DCPPINTEROP_ENABLE_TESTING=${CPPJIT_ENABLE_CPPINTEROP_TESTS} - -DBUILD_SHARED_LIBS=ON - -DCMAKE_INSTALL_PREFIX=${CPPINTEROP_INSTALL_DIR} - -DCMAKE_INSTALL_LIBDIR=lib - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_CXX_STANDARD=17 -) -if(Clang_DIR) - list(APPEND _interop_cmake_args -DClang_DIR=${Clang_DIR}) -endif() -if(CMAKE_C_COMPILER) - list(APPEND _interop_cmake_args -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}) -endif() -if(CMAKE_CXX_COMPILER) - list(APPEND _interop_cmake_args -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}) -endif() - -ExternalProject_Add(CppInterOp - GIT_REPOSITORY https://github.com/compiler-research/CppInterOp.git - GIT_TAG ${CPPINTEROP_GIT_TAG} - GIT_SHALLOW ON - PREFIX "${CMAKE_BINARY_DIR}/CppInterOp" - CMAKE_ARGS ${_interop_cmake_args} - BUILD_BYPRODUCTS - "${CPPINTEROP_INSTALL_DIR}/lib/libclangCppInterOp${CMAKE_SHARED_LIBRARY_SUFFIX}" - LOG_DOWNLOAD ON - LOG_CONFIGURE ON - LOG_BUILD ON - LOG_INSTALL ON - LOG_OUTPUT_ON_FAILURE ON -) +# Include cmake for CppInterOp config and build using ExternalProject. +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/AddCppInterOp.cmake) +cppjit_add_cppinterop() # this libcppyy.so merges both CPyCppyy and clingwrapper file(GLOB CPYCPPYY_SOURCES CONFIGURE_DEPENDS src/CPyCppyy/src/*.cxx) diff --git a/cmake/AddCppInterOp.cmake b/cmake/AddCppInterOp.cmake new file mode 100644 index 0000000..70148ae --- /dev/null +++ b/cmake/AddCppInterOp.cmake @@ -0,0 +1,75 @@ +# Configures the CppInterOp ExternalProject. Default backend is clang-repl; +# CPPJIT_USE_CLING builds CppInterOp against a provided cling build +include_guard(GLOBAL) +include(ExternalProject) + +# Developer toggle: Cling from either ROOT or standalone supplies LLVM_DIR/Clang_DIR +option(CPPJIT_USE_CLING "Build CppInterOp against a prebuilt Cling C++ Interpreter (ROOT)" OFF) +mark_as_advanced(CPPJIT_USE_CLING) + +set(Cling_DIR "" CACHE PATH "Cling CMake config dir; derived from LLVM_DIR when empty (cling mode only)") +# Surface Cling_DIR in ccmake/GUI only when cling mode is on. +if(CPPJIT_USE_CLING) + mark_as_advanced(CLEAR Cling_DIR) +else() + mark_as_advanced(Cling_DIR) +endif() + +function(cppjit_add_cppinterop) + set(_args + -DLLVM_DIR=${LLVM_DIR} + -DCPPINTEROP_ENABLE_TESTING=${CPPJIT_ENABLE_CPPINTEROP_TESTS} + -DBUILD_SHARED_LIBS=ON + -DCMAKE_INSTALL_PREFIX=${CPPINTEROP_INSTALL_DIR} + -DCMAKE_INSTALL_LIBDIR=lib + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_CXX_STANDARD=17 + ) + + if(CPPJIT_USE_CLING) + set(_cling_dir "${Cling_DIR}") + if(NOT _cling_dir) + # LLVM_DIR is /lib/cmake/llvm; cling config sits at the + # sibling /lib/cmake/cling (setup-llvm flavor:cling layout). + get_filename_component(_prefix "${LLVM_DIR}" DIRECTORY) # /lib/cmake + get_filename_component(_prefix "${_prefix}" DIRECTORY) # /lib + get_filename_component(_prefix "${_prefix}" DIRECTORY) # + set(_cling_dir "${_prefix}/lib/cmake/cling") + endif() + message(STATUS "CppInterOp backend: Cling (found at ${_cling_dir})") + list(APPEND _args + -DCPPINTEROP_USE_CLING=ON + -DCPPINTEROP_USE_REPL=OFF + -DCling_DIR=${_cling_dir} + ) + else() + list(APPEND _args + -DCPPINTEROP_USE_REPL=ON + -DCPPINTEROP_USE_CLING=OFF + ) + endif() + + if(Clang_DIR) + list(APPEND _args -DClang_DIR=${Clang_DIR}) + endif() + if(CMAKE_C_COMPILER) + list(APPEND _args -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}) + endif() + if(CMAKE_CXX_COMPILER) + list(APPEND _args -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}) + endif() + + ExternalProject_Add(CppInterOp + GIT_REPOSITORY https://github.com/compiler-research/CppInterOp.git + GIT_TAG ${CPPINTEROP_GIT_TAG} + PREFIX "${CMAKE_BINARY_DIR}/CppInterOp" + CMAKE_ARGS ${_args} + BUILD_BYPRODUCTS + "${CPPINTEROP_INSTALL_DIR}/lib/libclangCppInterOp${CMAKE_SHARED_LIBRARY_SUFFIX}" + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON + LOG_INSTALL ON + LOG_OUTPUT_ON_FAILURE ON + ) +endfunction() diff --git a/python/cppjit/cppyy/__init__.py b/python/cppjit/cppyy/__init__.py index 8ad8b5f..9ca8349 100644 --- a/python/cppjit/cppyy/__init__.py +++ b/python/cppjit/cppyy/__init__.py @@ -238,7 +238,7 @@ def cppexec(stmt): def evaluate(input): box = gbl.Cpp.Evaluate(input) # Truthy sentinel: skips Box::convertTo's UB-on-K_Unspecified arm. - if box.getKind() == gbl.CppImpl.Box.K_Unspecified: + if box.getKind() == gbl.Cpp.Box.K_Unspecified: return ~0 return box.convertTo['long']() diff --git a/src/CPyCppyy/include/CPyCppyy/API.h b/src/CPyCppyy/include/CPyCppyy/API.h index 71d9ba3..8f17b0f 100644 --- a/src/CPyCppyy/include/CPyCppyy/API.h +++ b/src/CPyCppyy/include/CPyCppyy/API.h @@ -28,16 +28,24 @@ #define CPYCPPYY_VERSION_HEX 0x011200 // Cppyy types +#ifndef CPYCPPYY_INTERNAL + +namespace Cpp { +struct DeclRef; +struct TypeRef; +struct FuncRef; +struct ObjectRef; +} // namespace Cpp + namespace Cppyy { - typedef void* TCppScope_t; - typedef TCppScope_t TCppType_t; - typedef void* TCppEnum_t; - typedef void* TCppObject_t; - typedef void* TCppMethod_t; - - typedef size_t TCppIndex_t; - typedef void* TCppFuncAddr_t; +typedef Cpp::DeclRef TCppScope_t; +typedef Cpp::TypeRef TCppType_t; +typedef Cpp::ObjectRef TCppObject_t; +typedef Cpp::FuncRef TCppMethod_t; +typedef size_t TCppIndex_t; +typedef void* TCppFuncAddr_t; } // namespace Cppyy +#endif // Bindings #include "CPyCppyy/CommonDefs.h" diff --git a/src/CPyCppyy/src/API.cxx b/src/CPyCppyy/src/API.cxx index 800df39..7c54dcf 100644 --- a/src/CPyCppyy/src/API.cxx +++ b/src/CPyCppyy/src/API.cxx @@ -1,5 +1,6 @@ // Bindings #include "CPyCppyy.h" +#include "Cppyy.h" #define CPYCPPYY_INTERNAL 1 #include "CPyCppyy/API.h" #undef CPYCPPYY_INTERNAL @@ -47,22 +48,10 @@ static bool Initialize() if (!Py_IsInitialized()) { // this happens if Cling comes in first -#if PY_VERSION_HEX < 0x03020000 - PyEval_InitThreads(); -#endif -#if PY_VERSION_HEX < 0x03080000 - Py_Initialize(); -#else PyConfig config; PyConfig_InitPythonConfig(&config); PyConfig_SetString(&config, &config.program_name, L"cppyy"); Py_InitializeFromConfig(&config); -#endif -#if PY_VERSION_HEX >= 0x03020000 -#if PY_VERSION_HEX < 0x03090000 - PyEval_InitThreads(); -#endif -#endif // try again to see if the interpreter is initialized if (!Py_IsInitialized()) { @@ -71,16 +60,6 @@ static bool Initialize() return false; } - // set the command line arguments on python's sys.argv -#if PY_VERSION_HEX < 0x03000000 - char* argv[] = {const_cast("cppyy")}; -#elif PY_VERSION_HEX < 0x03080000 - wchar_t* argv[] = {const_cast(L"cppyy")}; -#endif -#if PY_VERSION_HEX < 0x03080000 - PySys_SetArgv(sizeof(argv)/sizeof(argv[0]), argv); -#endif - // force loading of the cppyy module PyRun_SimpleString(const_cast("import cppyy")); } @@ -112,7 +91,7 @@ std::string CPyCppyy::Instance_GetScopedFinalName(PyObject* pyobject) return ""; } - Cppyy::TCppType_t pyobjectClass = ((CPPInstance *)pyobject)->ObjectIsA(); + Cppyy::TCppScope_t pyobjectClass = ((CPPInstance *)pyobject)->ObjectIsA(); return Cppyy::GetScopedFinalName(pyobjectClass); } diff --git a/src/CPyCppyy/src/CPPClassMethod.cxx b/src/CPyCppyy/src/CPPClassMethod.cxx index e7351a2..9134d5a 100644 --- a/src/CPyCppyy/src/CPPClassMethod.cxx +++ b/src/CPyCppyy/src/CPPClassMethod.cxx @@ -5,11 +5,8 @@ //- public members -------------------------------------------------------------- -PyObject* CPyCppyy::CPPClassMethod::Call(CPPInstance*& -#if PY_VERSION_HEX >= 0x03080000 - self -#endif - , CPyCppyy_PyArgs_t args, size_t nargsf, PyObject* kwds, CallContext* ctxt) +PyObject *CPyCppyy::CPPClassMethod::Call(CPPInstance *&self, CPyCppyy_PyArgs_t args, + size_t nargsf, PyObject *kwds, CallContext *ctxt) { // preliminary check in case keywords are accidently used (they are ignored otherwise) if (kwds && ((PyDict_Check(kwds) && PyDict_Size(kwds)) || @@ -23,7 +20,6 @@ PyObject* CPyCppyy::CPPClassMethod::Call(CPPInstance*& return nullptr; // translate the arguments -#if PY_VERSION_HEX >= 0x03080000 // TODO: The following is not robust and should be revisited e.g. by making CPPOverloads // that have only CPPClassMethods be true Python classmethods? Note that the original // implementation wasn't 100% correct either (e.g. static size() mapped to len()). @@ -40,7 +36,6 @@ PyObject* CPyCppyy::CPPClassMethod::Call(CPPInstance*& nargsf -= 1; } } -#endif if (!this->ConvertAndSetArgs(args, nargsf, ctxt)) return nullptr; diff --git a/src/CPyCppyy/src/CPPConstructor.cxx b/src/CPyCppyy/src/CPPConstructor.cxx index 5cd03df..7b6170f 100644 --- a/src/CPyCppyy/src/CPPConstructor.cxx +++ b/src/CPyCppyy/src/CPPConstructor.cxx @@ -156,7 +156,7 @@ PyObject* CPyCppyy::CPPConstructor::Call(CPPInstance*& self, // mark as actual to prevent needless auto-casting and register on its class self->fFlags |= CPPInstance::kIsActual; if (!(((CPPClass*)Py_TYPE(self))->fFlags & CPPScope::kIsSmart)) - MemoryRegulator::RegisterPyObject(self, (Cppyy::TCppObject_t)address); + MemoryRegulator::RegisterPyObject(self, Cppyy::TCppObject_t((void*)address)); // handling smart types this way is deeply fugly, but if CPPInstance sets the proper // types in op_new first, then the wrong init is called @@ -225,7 +225,6 @@ PyObject* CPyCppyy::CPPMultiConstructor::Call(CPPInstance*& self, // TODO: this way of forwarding is expensive as the loop is external to this call; // it would be more efficient to have the argument handling happen beforehand -#if PY_VERSION_HEX >= 0x03080000 // fetch self, verify, and put the arguments in usable order (if self is not handled // first, arguments can not be reordered with sentinels in place) PyCallArgs cargs{self, argsin, nargsf, kwds}; @@ -244,11 +243,6 @@ PyObject* CPyCppyy::CPPMultiConstructor::Call(CPPInstance*& self, // copy out self as it may have been updated self = cargs.fSelf; -#else - PyObject* args = argsin; - Py_INCREF(args); -#endif - if (PyTuple_CheckExact(args) && PyTuple_GET_SIZE(args)) { // case 0. falls through Py_ssize_t nArgs = PyTuple_GET_SIZE(args); @@ -311,20 +305,13 @@ PyObject* CPyCppyy::CPPMultiConstructor::Call(CPPInstance*& self, } } -#if PY_VERSION_HEX < 0x03080000 - Py_ssize_t -#endif nargs = PyTuple_GET_SIZE(args); -#if PY_VERSION_HEX >= 0x03080000 // now unroll the new args tuple into a vector of objects auto argsu = std::unique_ptr{new PyObject*[nargs]}; for (Py_ssize_t i = 0; i < nargs; ++i) argsu[i] = PyTuple_GET_ITEM(args, i); CPyCppyy_PyArgs_t _args = argsu.get(); -#else - CPyCppyy_PyArgs_t _args = args; -#endif PyObject* result = CPPConstructor::Call(self, _args, nargs, kwds, ctxt); Py_DECREF(args); @@ -339,11 +326,9 @@ PyObject* CPyCppyy::CPPAbstractClassConstructor::Call(CPPInstance*& self, { // do not allow instantiation of abstract classes if ((self && GetScope() != self->ObjectIsA() -#if PY_VERSION_HEX >= 0x03080000 ) || (!self && !(ctxt->fFlags & CallContext::kFromDescr) && \ CPyCppyy_PyArgs_GET_SIZE(args, nargsf) && CPPInstance_Check(args[0]) && \ GetScope() != ((CPPInstance*)args[0])->ObjectIsA() -#endif )) { // happens if a dispatcher is inserted; allow constructor call return CPPConstructor::Call(self, args, nargsf, kwds, ctxt); diff --git a/src/CPyCppyy/src/CPPDataMember.cxx b/src/CPyCppyy/src/CPPDataMember.cxx index 7b84b17..ae2e742 100644 --- a/src/CPyCppyy/src/CPPDataMember.cxx +++ b/src/CPyCppyy/src/CPPDataMember.cxx @@ -1,6 +1,7 @@ // Bindings #include "CPyCppyy.h" #include "CPyCppyy/Reflex.h" +#include "Cppyy.h" #include "PyStrings.h" #include "CPPDataMember.h" #include "CPPInstance.h" @@ -193,7 +194,7 @@ static CPPDataMember* dm_new(PyTypeObject* pytype, PyObject*, PyObject*) dm->fOffset = 0; dm->fFlags = 0; dm->fConverter = nullptr; - dm->fEnclosingScope = 0; + dm->fEnclosingScope = nullptr; dm->fDescription = nullptr; dm->fDoc = nullptr; @@ -297,25 +298,12 @@ PyTypeObject CPPDataMember_Type = { 0, // tp_mro 0, // tp_cache 0, // tp_subclasses - 0 // tp_weaklist -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, // tp_weaklist + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; } // namespace CPyCppyy @@ -416,7 +404,7 @@ void* CPyCppyy::CPPDataMember::GetAddress(CPPInstance* pyobj) // the proxy's internal offset is calculated from the enclosing class ptrdiff_t offset = 0; - Cppyy::TCppType_t oisa = pyobj->ObjectIsA(); + Cppyy::TCppScope_t oisa = pyobj->ObjectIsA(); if (oisa != fEnclosingScope) offset = Cppyy::GetBaseOffset(oisa, fEnclosingScope, obj, 1 /* up-cast */); diff --git a/src/CPyCppyy/src/CPPEnum.cxx b/src/CPyCppyy/src/CPPEnum.cxx index ca21f6a..a2598ef 100644 --- a/src/CPyCppyy/src/CPPEnum.cxx +++ b/src/CPyCppyy/src/CPPEnum.cxx @@ -4,6 +4,7 @@ #include "PyStrings.h" #include "TypeManip.h" #include "Utility.h" +#include //- private helpers ---------------------------------------------------------- @@ -32,11 +33,7 @@ PyObject* CPyCppyy::pyval_from_enum(const std::string& enum_type, PyObject* pyty PyObject* bval; if (enum_type == "char") { char val = (char)llval; -#if PY_VERSION_HEX < 0x03000000 - bval = CPyCppyy_PyText_FromStringAndSize(&val, 1); -#else bval = PyUnicode_FromOrdinal((int)val); -#endif } else if (enum_type == "int" || enum_type == "unsigned int") bval = PyInt_FromLong((long)llval); else @@ -97,7 +94,7 @@ static PyObject* enum_repr(PyObject* self) //---------------------------------------------------------------------------- // TODO: factor the following lookup with similar codes in Convertes and TemplateProxy.cxx -static std::map gCTypesNames = { +static std::unordered_map gCTypesNames = { {"bool", "c_bool"}, {"char", "c_char"}, {"wchar_t", "c_wchar"}, {"std::byte", "c_byte"}, {"int8_t", "c_byte"}, {"uint8_t", "c_ubyte"}, @@ -172,7 +169,7 @@ 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); + PyObject* pycppscope = PyLong_FromVoidPtr(etype.data); PyDict_SetItem(dct, PyStrings::gCppName, pycppname); PyDict_SetItem(dct, PyStrings::gThisModule, pycppscope); Py_DECREF(pycppname); @@ -203,7 +200,7 @@ CPyCppyy::CPPEnum* CPyCppyy::CPPEnum_New(const std::string& name, Cppyy::TCppSco // collect the enum values std::vector econstants = Cppyy::GetEnumConstants(etype); bool values_ok = true; - for (auto *econstant : econstants) { + for (auto econstant : econstants) { PyObject* val = pyval_from_enum(resolved, pyenum, pyside_type, econstant); if (!val) { values_ok = false; @@ -243,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/src/CPyCppyy/src/CPPExcInstance.cxx b/src/CPyCppyy/src/CPPExcInstance.cxx index fa17d43..07270fd 100644 --- a/src/CPyCppyy/src/CPPExcInstance.cxx +++ b/src/CPyCppyy/src/CPPExcInstance.cxx @@ -164,9 +164,6 @@ static PyNumberMethods ep_as_number = { 0, // nb_add 0, // nb_subtract 0, // nb_multiply -#if PY_VERSION_HEX < 0x03000000 - 0, // nb_divide -#endif 0, // nb_remainder 0, // nb_divmod 0, // nb_power @@ -180,46 +177,26 @@ static PyNumberMethods ep_as_number = { 0, // nb_and 0, // nb_xor 0, // nb_or -#if PY_VERSION_HEX < 0x03000000 - 0, // nb_coerce -#endif 0, // nb_int 0, // nb_long (nb_reserved in p3) 0, // nb_float -#if PY_VERSION_HEX < 0x03000000 - 0, // nb_oct - 0, // nb_hex -#endif 0, // nb_inplace_add 0, // nb_inplace_subtract 0, // nb_inplace_multiply -#if PY_VERSION_HEX < 0x03000000 - 0, // nb_inplace_divide -#endif 0, // nb_inplace_remainder 0, // nb_inplace_power 0, // nb_inplace_lshift 0, // nb_inplace_rshift 0, // nb_inplace_and 0, // nb_inplace_xor - 0 // nb_inplace_or -#if PY_VERSION_HEX >= 0x02020000 - , 0 // nb_floor_divide -#if PY_VERSION_HEX < 0x03000000 - , 0 // nb_true_divide -#else - , 0 // nb_true_divide -#endif - , 0 // nb_inplace_floor_divide - , 0 // nb_inplace_true_divide -#endif -#if PY_VERSION_HEX >= 0x02050000 - , 0 // nb_index -#endif -#if PY_VERSION_HEX >= 0x03050000 - , 0 // nb_matrix_multiply - , 0 // nb_inplace_matrix_multiply -#endif + 0, // nb_inplace_or + 0, // nb_floor_divide + 0, // nb_true_divide + 0, // nb_inplace_floor_divide + 0, // nb_inplace_true_divide + 0, // nb_index + 0, // nb_matrix_multiply + 0 // nb_inplace_matrix_multiply }; //= CPyCppyy exception object proxy type ====================================== @@ -272,25 +249,12 @@ PyTypeObject CPPExcInstance_Type = { 0, // tp_mro 0, // tp_cache 0, // tp_subclasses - 0 // tp_weaklist -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, // tp_weaklist + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; } // namespace CPyCppyy diff --git a/src/CPyCppyy/src/CPPFunction.cxx b/src/CPyCppyy/src/CPPFunction.cxx index 89e65c4..5cd8956 100644 --- a/src/CPyCppyy/src/CPPFunction.cxx +++ b/src/CPyCppyy/src/CPPFunction.cxx @@ -10,7 +10,6 @@ //- CFunction helpers ----------------------------------------------------------- bool CPyCppyy::AdjustSelf(PyCallArgs& cargs) { -#if PY_VERSION_HEX >= 0x03080000 if (cargs.fNArgsf & PY_VECTORCALL_ARGUMENTS_OFFSET) { // mutation allowed? std::swap(((PyObject**)cargs.fArgs-1)[0], (PyObject*&)cargs.fSelf); cargs.fFlags |= PyCallArgs::kSelfSwap; @@ -31,21 +30,6 @@ bool CPyCppyy::AdjustSelf(PyCallArgs& cargs) cargs.fFlags |= PyCallArgs::kDoFree; cargs.fNArgsf += 1; } -#else - Py_ssize_t sz = PyTuple_GET_SIZE(cargs.fArgs); - CPyCppyy_PyArgs_t newArgs = PyTuple_New(sz+1); - for (int i = 0; i < sz; ++i) { - PyObject* item = PyTuple_GET_ITEM(cargs.fArgs, i); - Py_INCREF(item); - PyTuple_SET_ITEM(newArgs, i+1, item); - } - Py_INCREF(cargs.fSelf); - PyTuple_SET_ITEM(newArgs, 0, (PyObject*)cargs.fSelf); - - cargs.fArgs = newArgs; - cargs.fFlags |= PyCallArgs::kDoDecref; - cargs.fNArgsf += 1; -#endif return true; } @@ -73,14 +57,12 @@ PyObject* CPyCppyy::CPPFunction::Call(CPPInstance*& self, return nullptr; } -#if PY_VERSION_HEX >= 0x03080000 // special case, if this method was inserted as a constructor, then self is nullptr // and it will be the first argument and needs to be used as Python context if (IsConstructor(ctxt->fFlags) && !ctxt->fPyContext && \ CPyCppyy_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf)) { ctxt->fPyContext = cargs.fArgs[0]; } -#endif // translate the arguments as normal if (!this->ConvertAndSetArgs(cargs.fArgs, cargs.fNArgsf, ctxt)) @@ -89,7 +71,6 @@ PyObject* CPyCppyy::CPPFunction::Call(CPPInstance*& self, // execute function PyObject* result = this->Execute(nullptr, 0, ctxt); -#if PY_VERSION_HEX >= 0x03080000 // special case, if this method was inserted as a constructor, then if no self was // provided, it will be the first argument and may have been updated if (IsConstructor(ctxt->fFlags) && result && !cargs.fSelf && \ @@ -97,7 +78,6 @@ PyObject* CPyCppyy::CPPFunction::Call(CPPInstance*& self, self = (CPPInstance*)cargs.fArgs[0]; Py_INCREF(self); } -#endif return result; } @@ -121,11 +101,7 @@ bool CPyCppyy::CPPReverseBinary::ProcessArgs(PyCallArgs& cargs) } // swap the arguments -#if PY_VERSION_HEX >= 0x03080000 std::swap(((PyObject**)cargs.fArgs)[0], ((PyObject**)cargs.fArgs)[1]); -#else - std::swap(PyTuple_GET_ITEM(cargs.fArgs, 0), PyTuple_GET_ITEM(cargs.fArgs, 1)); -#endif cargs.fFlags |= PyCallArgs::kArgsSwap; return true; diff --git a/src/CPyCppyy/src/CPPGetSetItem.cxx b/src/CPyCppyy/src/CPPGetSetItem.cxx index 2ed852f..5f4eff1 100644 --- a/src/CPyCppyy/src/CPPGetSetItem.cxx +++ b/src/CPyCppyy/src/CPPGetSetItem.cxx @@ -64,22 +64,12 @@ bool CPyCppyy::CPPSetItem::ProcessArgs(PyCallArgs& cargs) } // unroll any tuples, if present in the arguments -#if PY_VERSION_HEX >= 0x03080000 if (realsize != nArgs-1) { CPyCppyy_PyArgs_t unrolled = (PyObject**)PyMem_Malloc(realsize * sizeof(PyObject*)); unroll(cargs.fArgs, unrolled, nArgs-1); cargs.fArgs = unrolled; cargs.fFlags |= PyCallArgs::kDoFree; } -#else - if (realsize != nArgs-1) { - CPyCppyy_PyArgs_t unrolled = PyTuple_New(realsize); - unroll(cargs.fArgs, unrolled, nArgs-1); - cargs.fArgs = unrolled; - } else - cargs.fArgs = PyTuple_GetSlice(cargs.fArgs, 0, nArgs-1); - cargs.fFlags |= PyCallArgs::kDoDecref; -#endif cargs.fNArgsf = realsize; // continue normal method processing @@ -103,13 +93,8 @@ bool CPyCppyy::CPPGetItem::ProcessArgs(PyCallArgs& cargs) // unroll any tuples, if present in the arguments if (realsize != nArgs) { CPyCppyy_PyArgs_t packed_args = cargs.fArgs; -#if PY_VERSION_HEX >= 0x03080000 cargs.fArgs = (PyObject**)PyMem_Malloc(realsize * sizeof(PyObject*)); cargs.fFlags |= PyCallArgs::kDoFree; -#else - cargs.fArgs = PyTuple_New(realsize); - cargs.fFlags |= PyCallArgs::kDoDecref; -#endif cargs.fNArgsf = realsize; unroll(packed_args, cargs.fArgs, nArgs); } diff --git a/src/CPyCppyy/src/CPPInstance.cxx b/src/CPyCppyy/src/CPPInstance.cxx index ef7841c..a7f0141 100644 --- a/src/CPyCppyy/src/CPPInstance.cxx +++ b/src/CPyCppyy/src/CPPInstance.cxx @@ -3,6 +3,7 @@ #include "CPPInstance.h" #include "CPPScope.h" #include "CPPOverload.h" +#include "Cppyy.h" #include "MemoryRegulator.h" #include "ProxyWrappers.h" #include "PyStrings.h" @@ -181,12 +182,22 @@ void CPyCppyy::CPPInstance::SetSmart(PyObject* smart_type) } //---------------------------------------------------------------------------- -Cppyy::TCppType_t CPyCppyy::CPPInstance::GetSmartIsA() const +Cppyy::TCppScope_t CPyCppyy::CPPInstance::GetSmartIsA() const { - if (!IsSmart()) return (Cppyy::TCppType_t)0; + if (!IsSmart()) return Cppyy::TCppScope_t{}; return SMART_TYPE(this); } +//---------------------------------------------------------------------------- +Cppyy::TCppScope_t CPyCppyy::CPPInstance::GetSmartUnderlyingType() const +{ +// Declared underlying type of the embedded smart pointer ('Base' for a +// std::unique_ptr), independent of any downcast of the dereferenced +// object. + if (!IsSmart()) return Cppyy::TCppScope_t{}; + return SMART_CLS(this)->fUnderlyingType; +} + //---------------------------------------------------------------------------- CPyCppyy::CI_DatamemberCache_t& CPyCppyy::CPPInstance::GetDatamemberCache() { @@ -208,7 +219,7 @@ void CPyCppyy::CPPInstance::SetDispatchPtr(void* ptr) void CPyCppyy::op_dealloc_nofree(CPPInstance* pyobj) { // Destroy the held C++ object, if owned; does not deallocate the proxy. - Cppyy::TCppType_t klass = pyobj->ObjectIsA(false /* check_smart */); + Cppyy::TCppScope_t klass = pyobj->ObjectIsA(false /* check_smart */); void*& cppobj = pyobj->GetObjectRaw(); if (pyobj->fFlags & CPPInstance::kIsRegulated) @@ -655,7 +666,7 @@ static PyObject* op_repr(CPPInstance* self) return PyBaseObject_Type.tp_repr((PyObject*)self); PyObject* modname = PyObject_GetAttr(pyclass, PyStrings::gModule); - Cppyy::TCppType_t klass = self->ObjectIsA(); + Cppyy::TCppScope_t klass = self->ObjectIsA(); std::string clName = klass ? Cppyy::GetFinalName(klass) : ""; if (self->fFlags & CPPInstance::kIsPtrPtr) clName.append("**"); @@ -737,21 +748,17 @@ static PyObject* op_str_internal(PyObject* pyobj, PyObject* lshift, bool isBound std::ostringstream s; PyObject* pys = BindCppObjectNoCast(&s, sOStringStreamID); Py_INCREF(pys); -#if PY_VERSION_HEX >= 0x03000000 // for py3 and later, a ref-count of 2 is okay to consider the object temporary, but // in this case, we can't lose our existing ostrinstring (otherwise, we'd have to peel // it out of the return value, if moves are used Py_INCREF(pys); -#endif PyObject* res; if (isBound) res = PyObject_CallFunctionObjArgs(lshift, pys, NULL); else res = PyObject_CallFunctionObjArgs(lshift, pys, pyobj, NULL); Py_DECREF(pys); -#if PY_VERSION_HEX >= 0x03000000 Py_DECREF(pys); -#endif if (res) { Py_DECREF(res); @@ -1003,9 +1010,6 @@ static PyNumberMethods op_as_number = { (binaryfunc)op_add_stub, // nb_add (binaryfunc)op_sub_stub, // nb_subtract (binaryfunc)op_mul_stub, // nb_multiply -#if PY_VERSION_HEX < 0x03000000 - (binaryfunc)op_div_stub, // nb_divide -#endif 0, // nb_remainder 0, // nb_divmod 0, // nb_power @@ -1019,46 +1023,26 @@ static PyNumberMethods op_as_number = { 0, // nb_and 0, // nb_xor 0, // nb_or -#if PY_VERSION_HEX < 0x03000000 - 0, // nb_coerce -#endif 0, // nb_int 0, // nb_long (nb_reserved in p3) 0, // nb_float -#if PY_VERSION_HEX < 0x03000000 - 0, // nb_oct - 0, // nb_hex -#endif 0, // nb_inplace_add 0, // nb_inplace_subtract 0, // nb_inplace_multiply -#if PY_VERSION_HEX < 0x03000000 - 0, // nb_inplace_divide -#endif 0, // nb_inplace_remainder 0, // nb_inplace_power 0, // nb_inplace_lshift 0, // nb_inplace_rshift 0, // nb_inplace_and 0, // nb_inplace_xor - 0 // nb_inplace_or -#if PY_VERSION_HEX >= 0x02020000 - , 0 // nb_floor_divide -#if PY_VERSION_HEX < 0x03000000 - , 0 // nb_true_divide -#else - , (binaryfunc)op_div_stub // nb_true_divide -#endif - , 0 // nb_inplace_floor_divide - , 0 // nb_inplace_true_divide -#endif -#if PY_VERSION_HEX >= 0x02050000 - , 0 // nb_index -#endif -#if PY_VERSION_HEX >= 0x03050000 - , 0 // nb_matrix_multiply - , 0 // nb_inplace_matrix_multiply -#endif + 0, // nb_inplace_or + 0, // nb_floor_divide + (binaryfunc)op_div_stub, // nb_true_divide + 0, // nb_inplace_floor_divide + 0, // nb_inplace_true_divide + 0, // nb_index + 0, // nb_matrix_multiply + 0 // nb_inplace_matrix_multiply }; @@ -1111,25 +1095,12 @@ PyTypeObject CPPInstance_Type = { 0, // tp_mro 0, // tp_cache 0, // tp_subclasses - 0 // tp_weaklist -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, // tp_weaklist + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; } // namespace CPyCppyy diff --git a/src/CPyCppyy/src/CPPInstance.h b/src/CPyCppyy/src/CPPInstance.h index a1aa489..3482cd0 100644 --- a/src/CPyCppyy/src/CPPInstance.h +++ b/src/CPyCppyy/src/CPPInstance.h @@ -76,7 +76,8 @@ class CPPInstance { // smart pointer management void SetSmart(PyObject* smart_type); void* GetSmartObject() { return GetObjectRaw(); } - Cppyy::TCppType_t GetSmartIsA() const; + Cppyy::TCppScope_t GetSmartIsA() const; + Cppyy::TCppScope_t GetSmartUnderlyingType() const; // cross-inheritance dispatch void SetDispatchPtr(void*); diff --git a/src/CPyCppyy/src/CPPMethod.cxx b/src/CPyCppyy/src/CPPMethod.cxx index d0d5d53..5bbf30c 100644 --- a/src/CPyCppyy/src/CPPMethod.cxx +++ b/src/CPyCppyy/src/CPPMethod.cxx @@ -4,6 +4,7 @@ #include "CPPExcInstance.h" #include "CPPInstance.h" #include "Converters.h" +#include "Cppyy.h" #include "Executors.h" #include "ProxyWrappers.h" #include "PyStrings.h" @@ -40,7 +41,6 @@ CPyCppyy::PyCallArgs::~PyCallArgs() { if (fFlags & kSelfSwap) // if self swap, fArgs has been offset by -1 std::swap((PyObject*&)fSelf, ((PyObject**)fArgs)[0]); -#if PY_VERSION_HEX >= 0x03080000 if (fFlags & kIsOffset) fArgs -= 1; if (fFlags & kDoItemDecref) { @@ -55,12 +55,6 @@ CPyCppyy::PyCallArgs::~PyCallArgs() { int offset = (fFlags & kSelfSwap) ? 1 : 0; std::swap(((PyObject**)fArgs+offset)[0], ((PyObject**)fArgs+offset)[1]); } -#else - if (fFlags & kDoDecref) - Py_DECREF((PyObject*)fArgs); - else if (fFlags & kArgsSwap) - std::swap(PyTuple_GET_ITEM(fArgs, 0), PyTuple_GET_ITEM(fArgs, 1)); -#endif } @@ -120,13 +114,13 @@ inline PyObject* CPyCppyy::CPPMethod::ExecuteFast( PyObject* result = nullptr; try { // C++ try block - result = fExecutor->Execute(fMethod, (Cppyy::TCppObject_t)((intptr_t)self+offset), ctxt); + result = fExecutor->Execute(fMethod, Cppyy::TCppObject_t((void*)((intptr_t)self+offset)), ctxt); } catch (PyException&) { ctxt->fFlags |= CallContext::kPyException; 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::GetFullScope("std::exception"); + static Cppyy::TCppScope_t exc_type = Cppyy::GetFullScope("std::exception"); ctxt->fFlags |= CallContext::kCppException; @@ -135,7 +129,7 @@ inline PyObject* CPyCppyy::CPPMethod::ExecuteFast( // TODO: factor this code with the same in ProxyWrappers (and cache it there to be able to // look up based on TCppType_t): - Cppyy::TCppType_t actual = Cppyy::GetActualClass(exc_type, &e); + Cppyy::TCppScope_t actual = Cppyy::GetActualClass(exc_type, &e); const std::string& finalname = Cppyy::GetScopedFinalName(actual); const std::string& parentname = TypeManip::extract_namespace(finalname); PyObject* parent = CreateScopeProxy(parentname); @@ -255,7 +249,7 @@ bool CPyCppyy::CPPMethod::InitConverters_() bool CPyCppyy::CPPMethod::InitExecutor_(Executor*& executor, CallContext* /* ctxt */) { // install executor conform to the return type - executor = + executor = (bool)fMethod == true ? CreateExecutor(Cppyy::GetMethodReturnType(fMethod)) \ : CreateExecutor(Cppyy::GetScopedFinalName(fScope)); @@ -369,7 +363,7 @@ void CPyCppyy::CPPMethod::SetPyError_(PyObject* msg) Py_XDECREF(msg); } -extern std::map TypeReductionMap; +extern std::unordered_map TypeReductionMap; //- constructors and destructor ---------------------------------------------- CPyCppyy::CPPMethod::CPPMethod( @@ -414,14 +408,14 @@ CPyCppyy::CPPMethod::~CPPMethod() //- public members ----------------------------------------------------------- /** * @brief Construct a Python string from the method's prototype - * + * * @param fa Show formal arguments of the method * @return PyObject* A Python string with the full method prototype, namespaces included. - * + * * For example, given: - * + * * int foo(int x); - * + * * namespace a { * namespace b { * namespace c { @@ -429,7 +423,7 @@ CPyCppyy::CPPMethod::~CPPMethod() * }}} * * This function returns: - * + * * 'int foo(int x)' * 'int a::b::c::foo(int x)' */ @@ -444,7 +438,7 @@ PyObject* CPyCppyy::CPPMethod::GetPrototype(bool fa) return CPyCppyy_PyText_FromFormat("%s%s %s%s", (Cppyy::IsStaticMethod(fMethod) ? "static " : ""), Cppyy::GetMethodReturnTypeAsString(fMethod).c_str(), - Cppyy::GetScopedFinalName(fMethod).c_str(), + Cppyy::GetScopedFinalName(Cppyy::TCppScope_t(fMethod.data)).c_str(), GetSignatureString(fa).c_str()); } @@ -465,7 +459,7 @@ PyObject* CPyCppyy::CPPMethod::Reflex(Cppyy::Reflex::RequestId_t request, Cppyy: if (request == Cppyy::Reflex::RETURN_TYPE) { std::string rtn = GetReturnTypeName(); - Cppyy::TCppScope_t scope = 0; + Cppyy::TCppScope_t scope = nullptr; if (format == Cppyy::Reflex::OPTIMAL || format == Cppyy::Reflex::AS_TYPE) scope = Cppyy::GetScope(rtn); @@ -583,7 +577,7 @@ int CPyCppyy::CPPMethod::GetPriority() priority += ((int)Cppyy::GetMethodReqArgs(fMethod) - (int)nArgs); // add a small penalty to prefer non-const methods over const ones for get/setitem - if (Cppyy::IsConstMethod(fMethod) && Cppyy::GetMethodName(fMethod) == "operator[]") + if (Cppyy::IsConstMethod(fMethod) && Cppyy::GetName(Cppyy::TCppScope_t(fMethod.data)) == "operator[]") priority += -10; // constructors are prefered @@ -707,11 +701,7 @@ PyObject* CPyCppyy::CPPMethod::GetArgDefault(int iarg, bool silent) PyObject* pycode = Py_CompileString((char*)defvalue.c_str(), "cppyy_default_compiler", Py_eval_input); if (pycode) { - pyval = PyEval_EvalCode( -#if PY_VERSION_HEX < 0x03000000 - (PyCodeObject*) -#endif - pycode, gdct, gdct); + pyval = PyEval_EvalCode(pycode, gdct, gdct); Py_DECREF(pycode); } @@ -755,11 +745,11 @@ int CPyCppyy::CPPMethod::GetArgMatchScore(PyObject* args_tuple) Py_ssize_t n = PyTuple_Size(args_tuple); int req_args = Cppyy::GetMethodReqArgs(fMethod); - + // Not enough arguments supplied: no match if (req_args > n) return INT_MAX; - + size_t score = 0; for (int i = 0; i < n; i++) { PyObject *pItem = PyTuple_GetItem(args_tuple, i); @@ -805,25 +795,17 @@ bool CPyCppyy::CPPMethod::Initialize(CallContext* ctxt) //---------------------------------------------------------------------------- bool CPyCppyy::CPPMethod::ProcessKwds(PyObject* self_in, PyCallArgs& cargs) { -#if PY_VERSION_HEX >= 0x03080000 if (!PyTuple_CheckExact(cargs.fKwds)) { SetPyError_(CPyCppyy_PyText_FromString("received unknown keyword names object")); return false; } Py_ssize_t nKeys = PyTuple_GET_SIZE(cargs.fKwds); -#else - if (!PyDict_CheckExact(cargs.fKwds)) { - SetPyError_(CPyCppyy_PyText_FromString("received unknown keyword arguments object")); - return false; - } - Py_ssize_t nKeys = PyDict_Size(cargs.fKwds); -#endif if (nKeys == 0 && !self_in) return true; if (!fArgIndices) { - fArgIndices = new std::map{}; + fArgIndices = new std::unordered_map{}; for (int iarg = 0; iarg < (int)Cppyy::GetMethodNumArgs(fMethod); ++iarg) (*fArgIndices)[Cppyy::GetMethodArgName(fMethod, iarg)] = iarg; } @@ -838,15 +820,10 @@ bool CPyCppyy::CPPMethod::ProcessKwds(PyObject* self_in, PyCallArgs& cargs) PyObject *key, *value; Py_ssize_t maxpos = -1; -#if PY_VERSION_HEX >= 0x03080000 Py_ssize_t npos_args = CPyCppyy_PyArgs_GET_SIZE(cargs.fArgs, cargs.fNArgsf); for (Py_ssize_t ikey = 0; ikey < nKeys; ++ikey) { key = PyTuple_GET_ITEM(cargs.fKwds, ikey); value = cargs.fArgs[npos_args+ikey]; -#else - Py_ssize_t pos = 0; - while (PyDict_Next(cargs.fKwds, &pos, &key, &value)) { -#endif const char* ckey = CPyCppyy_PyText_AsStringChecked(key); if (!ckey) return false; @@ -854,7 +831,7 @@ bool CPyCppyy::CPPMethod::ProcessKwds(PyObject* self_in, PyCallArgs& cargs) auto p = fArgIndices->find(ckey); if (p == fArgIndices->end()) { SetPyError_(CPyCppyy_PyText_FromFormat("%s::%s got an unexpected keyword argument \'%s\'", - Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(), ckey)); + Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetName(Cppyy::TCppScope_t(fMethod.data)).c_str(), ckey)); return false; } @@ -882,7 +859,7 @@ bool CPyCppyy::CPPMethod::ProcessKwds(PyObject* self_in, PyCallArgs& cargs) for (Py_ssize_t i = start; i < nArgs; ++i) { if (vArgs[i]) { SetPyError_(CPyCppyy_PyText_FromFormat("%s::%s got multiple values for argument %d", - Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(), (int)i+1)); + Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetName(Cppyy::TCppScope_t(fMethod.data)).c_str(), (int)i+1)); CPyCppyy_PyArgs_DEL(newArgs); return false; } @@ -909,23 +886,15 @@ bool CPyCppyy::CPPMethod::ProcessKwds(PyObject* self_in, PyCallArgs& cargs) } } -#if PY_VERSION_HEX >= 0x03080000 if (cargs.fFlags & PyCallArgs::kDoFree) { if (cargs.fFlags & PyCallArgs::kIsOffset) cargs.fArgs -= 1; -#else - if (cargs.fFlags & PyCallArgs::kDoDecref) { -#endif CPyCppyy_PyArgs_DEL(cargs.fArgs); } cargs.fArgs = newArgs; cargs.fNArgsf = maxargs; -#if PY_VERSION_HEX >= 0x03080000 cargs.fFlags = PyCallArgs::kDoFree | PyCallArgs::kDoItemDecref; -#else - cargs.fFlags = PyCallArgs::kDoDecref; -#endif return true; } @@ -945,9 +914,9 @@ 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(); + Cppyy::TCppScope_t oisa = pyobj->ObjectIsA(); if (fScope == Cppyy::GetGlobalScope() || // free global - oisa == 0 || // null pointer or ctor call + oisa == nullptr || // null pointer or ctor call oisa == fScope || // matching types Cppyy::IsSubclass(oisa, fScope)) { // id. @@ -956,15 +925,8 @@ bool CPyCppyy::CPPMethod::ProcessArgs(PyCallArgs& cargs) cargs.fSelf = pyobj; // offset args by 1 -#if PY_VERSION_HEX >= 0x03080000 cargs.fArgs += 1; cargs.fFlags |= PyCallArgs::kIsOffset; -#else - if (cargs.fFlags & PyCallArgs::kDoDecref) - Py_DECREF((PyObject*)cargs.fArgs); - cargs.fArgs = PyTuple_GetSlice(cargs.fArgs, 1, PyTuple_GET_SIZE(cargs.fArgs)); - cargs.fFlags |= PyCallArgs::kDoDecref; -#endif cargs.fNArgsf -= 1; // put the keywords, if any, in their places in the arguments array @@ -978,7 +940,7 @@ bool CPyCppyy::CPPMethod::ProcessArgs(PyCallArgs& cargs) // no self, set error and lament SetPyError_(CPyCppyy_PyText_FromFormat( "unbound method %s::%s must be called with a %s instance as first argument", - Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetMethodName(fMethod).c_str(), + Cppyy::GetFinalName(fScope).c_str(), Cppyy::GetName(Cppyy::TCppScope_t(fMethod.data)).c_str(), Cppyy::GetFinalName(fScope).c_str())); return false; } @@ -1064,12 +1026,20 @@ PyObject* CPyCppyy::CPPMethod::Call(CPPInstance*& self, } // get its class - Cppyy::TCppType_t derived = self->ObjectIsA(); + Cppyy::TCppScope_t derived = self->ObjectIsA(); + +// calculate offset: the generated wrapper casts 'this' to the method's actual +// declaring class, which may differ from fScope. In particular, a method +// brought into fScope through a using-declaration (e.g. `using Base::meth;`) +// is still declared in the base, so 'this' has to be adjusted to that base's +// subobject. Using fScope here would yield a zero offset and corrupt memory. + Cppyy::TCppScope_t declaring = Cppyy::GetParentScope(Cppyy::TCppScope_t(fMethod.data)); + if (!declaring) + declaring = fScope; -// calculate offset (the method expects 'this' to be an object of fScope) ptrdiff_t offset = 0; - if (derived && derived != fScope) - offset = Cppyy::GetBaseOffset(derived, fScope, object, 1 /* up-cast */); + if (derived && derived != declaring) + offset = Cppyy::GetBaseOffset(derived, declaring, object, 1 /* up-cast */); // actual call; recycle self instead of returning new object for same address objects CPPInstance* pyobj = (CPPInstance*)Execute(object, offset, ctxt); diff --git a/src/CPyCppyy/src/CPPMethod.h b/src/CPyCppyy/src/CPPMethod.h index e9558f5..e13a8da 100644 --- a/src/CPyCppyy/src/CPPMethod.h +++ b/src/CPyCppyy/src/CPPMethod.h @@ -5,7 +5,7 @@ #include "PyCallable.h" // Standard -#include +#include #include #include @@ -26,12 +26,8 @@ class PyCallArgs { kIsOffset = 0x0001, // args were offset by 1 to drop self kSelfSwap = 0x0002, // args[-1] and self need swapping kArgsSwap = 0x0004, // args[0] and args[1] need swapping -#if PY_VERSION_HEX >= 0x03080000 kDoFree = 0x0008, // args need to be free'd (vector call only) kDoItemDecref = 0x0010 // items in args need a decref (vector call only) -#else - kDoDecref = 0x0020 // args need a decref -#endif }; public: @@ -113,7 +109,7 @@ class CPPMethod : public PyCallable { // call dispatch buffers std::vector fConverters; - std::map* fArgIndices; + std::unordered_map* fArgIndices; protected: // cached value that doubles as initialized flag (uninitialized if -1) diff --git a/src/CPyCppyy/src/CPPOperator.cxx b/src/CPyCppyy/src/CPPOperator.cxx index 4832136..bf2aa60 100644 --- a/src/CPyCppyy/src/CPPOperator.cxx +++ b/src/CPyCppyy/src/CPPOperator.cxx @@ -14,11 +14,7 @@ CPyCppyy::CPPOperator::CPPOperator( if (name == "__mul__") fStub = CPPInstance_Type.tp_as_number->nb_multiply; else if (name == CPPYY__div__) -#if PY_VERSION_HEX < 0x03000000 - fStub = CPPInstance_Type.tp_as_number->nb_divide; -#else fStub = CPPInstance_Type.tp_as_number->nb_true_divide; -#endif else if (name == "__add__") fStub = CPPInstance_Type.tp_as_number->nb_add; else if (name == "__sub__") @@ -42,11 +38,9 @@ PyObject* CPyCppyy::CPPOperator::Call(CPPInstance*& self, Py_ssize_t idx_other = 0; if (CPyCppyy_PyArgs_GET_SIZE(args, nargsf) != 1) { -#if PY_VERSION_HEX >= 0x03080000 if ((CPyCppyy_PyArgs_GET_SIZE(args, nargsf) == 2 && CPyCppyy_PyArgs_GET_ITEM(args, 0) == (PyObject*)self)) idx_other = 1; else -#endif return result; } diff --git a/src/CPyCppyy/src/CPPOverload.cxx b/src/CPyCppyy/src/CPPOverload.cxx index 649c114..b4fb1da 100644 --- a/src/CPyCppyy/src/CPPOverload.cxx +++ b/src/CPyCppyy/src/CPPOverload.cxx @@ -2,13 +2,9 @@ #include "CPyCppyy.h" #include "CPyCppyy/Reflex.h" #include "structmember.h" // from Python -#if PY_VERSION_HEX >= 0x02050000 #if PY_VERSION_HEX < 0x030b0000 #include "code.h" // from Python #endif -#else -#include "compile.h" // from Python -#endif #ifndef CO_NOFREE // python2.2 does not have CO_NOFREE defined #define CO_NOFREE 0x0040 @@ -103,7 +99,6 @@ class TPythonCallback : public PyCallable { PyObject* Call(CPPInstance*& self, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject* kwds, CallContext* /* ctxt = 0 */) override { -#if PY_VERSION_HEX >= 0x03080000 if (self) { if (nargsf & PY_VECTORCALL_ARGUMENTS_OFFSET) { // mutation allowed? std::swap(((PyObject**)args-1)[0], (PyObject*&)self); @@ -130,25 +125,6 @@ class TPythonCallback : public PyCallable { std::swap(((PyObject**)args-1)[0], (PyObject*&)self); else PyMem_Free((void*)args); } -#else - PyObject* newArgs = nullptr; - if (self) { - Py_ssize_t nargs = PyTuple_Size(args); - newArgs = PyTuple_New(nargs+1); - Py_INCREF(self); - PyTuple_SET_ITEM(newArgs, 0, (PyObject*)self); - for (Py_ssize_t iarg = 0; iarg < nargs; ++iarg) { - PyObject* pyarg = PyTuple_GET_ITEM(args, iarg); - Py_INCREF(pyarg); - PyTuple_SET_ITEM(newArgs, iarg+1, pyarg); - } - } else { - Py_INCREF(args); - newArgs = args; - } - PyObject* result = PyObject_Call(fCallable, newArgs, kwds); - Py_DECREF(newArgs); -#endif return result; } }; @@ -396,83 +372,12 @@ static PyObject* mp_func_closure(CPPOverload* /* pymeth */, void*) Py_RETURN_NONE; } -// To declare a variable as unused only when compiling for Python 3. -#if PY_VERSION_HEX < 0x03000000 -#define CPyCppyy_Py3_UNUSED(name) name -#else -#define CPyCppyy_Py3_UNUSED(name) -#endif - //---------------------------------------------------------------------------- -static PyObject* mp_func_code(CPPOverload* CPyCppyy_Py3_UNUSED(pymeth), void*) +static PyObject* mp_func_code(CPPOverload*, void*) { // Code details are used in module inspect to fill out interactive help() -#if PY_VERSION_HEX < 0x03000000 - CPPOverload::Methods_t& methods = pymeth->fMethodInfo->fMethods; - -// collect arguments only if there is just 1 overload, otherwise put in a -// fake *args (see below for co_varnames) - PyObject* co_varnames = methods.size() == 1 ? methods[0]->GetCoVarNames() : nullptr; - if (!co_varnames) { - // TODO: static methods need no 'self' (but is harmless otherwise) - co_varnames = PyTuple_New(1 /* self */ + 1 /* fake */); - PyTuple_SET_ITEM(co_varnames, 0, CPyCppyy_PyText_FromString("self")); - PyTuple_SET_ITEM(co_varnames, 1, CPyCppyy_PyText_FromString("*args")); - } - - int co_argcount = (int)PyTuple_Size(co_varnames); - -// for now, code object representing the statement 'pass' - PyObject* co_code = PyString_FromStringAndSize("d\x00\x00S", 4); - -// tuples with all the const literals used in the function - PyObject* co_consts = PyTuple_New(0); - PyObject* co_names = PyTuple_New(0); - -// names, freevars, and cellvars go unused - PyObject* co_unused = PyTuple_New(0); - -// filename is made-up - PyObject* co_filename = PyString_FromString("cppyy.py"); - -// name is the function name, also through __name__ on the function itself - PyObject* co_name = PyString_FromString(pymeth->GetName().c_str()); - -// firstlineno is the line number of first function code in the containing scope - -// lnotab is a packed table that maps instruction count and line number - PyObject* co_lnotab = PyString_FromString("\x00\x01\x0c\x01"); - - PyObject* code = (PyObject*)PyCode_New( - co_argcount, // argcount - co_argcount+1, // nlocals - 2, // stacksize - CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE, // flags - co_code, // code - co_consts, // consts - co_names, // names - co_varnames, // varnames - co_unused, // freevars - co_unused, // cellvars - co_filename, // filename - co_name, // name - 1, // firstlineno - co_lnotab); // lnotab - - Py_DECREF(co_lnotab); - Py_DECREF(co_name); - Py_DECREF(co_unused); - Py_DECREF(co_filename); - Py_DECREF(co_varnames); - Py_DECREF(co_names); - Py_DECREF(co_consts); - Py_DECREF(co_code); - - return code; -#else // not important for functioning of most code, so not implemented for p3 for now (TODO) Py_RETURN_NONE; -#endif } //---------------------------------------------------------------------------- @@ -661,17 +566,9 @@ static PyGetSetDef mp_getset[] = { }; //= CPyCppyy method proxy function behavior ================================== -#if PY_VERSION_HEX >= 0x03080000 static PyObject* mp_vectorcall( CPPOverload* pymeth, PyObject* const *args, size_t nargsf, PyObject* kwds) -#else -static PyObject* mp_call(CPPOverload* pymeth, PyObject* args, PyObject* kwds) -#endif { -#if PY_VERSION_HEX < 0x03080000 - size_t nargsf = PyTuple_GET_SIZE(args); -#endif - // Call the appropriate overload of this method. // If called from a descriptor, then this could be a bound function with @@ -840,15 +737,6 @@ static CPPOverload* mp_descr_get(CPPOverload* pymeth, CPPInstance* pyobj, PyObje // py3.8 <= | vector calls no longer call the descriptor, so when it is // | called, the method is likely stored, so should be new object -#if PY_VERSION_HEX < 0x03080000 - if (!pyobj || (PyObject*)pyobj == Py_None /* from unbound TemplateProxy */) { - Py_XDECREF(pymeth->fSelf); pymeth->fSelf = nullptr; - pymeth->fFlags |= CallContext::kCallDirect | CallContext::kFromDescr; - Py_INCREF(pymeth); - return pymeth; // unbound, e.g. free functions - } -#endif - // create a new method object bool gc_track = false; CPPOverload* newPyMeth = free_list; @@ -867,7 +755,6 @@ static CPPOverload* mp_descr_get(CPPOverload* pymeth, CPPInstance* pyobj, PyObje *pymeth->fMethodInfo->fRefCount += 1; newPyMeth->fMethodInfo = pymeth->fMethodInfo; -#if PY_VERSION_HEX >= 0x03080000 newPyMeth->fVectorCall = pymeth->fVectorCall; if (pyobj && (PyObject*)pyobj != Py_None) { @@ -883,17 +770,6 @@ static CPPOverload* mp_descr_get(CPPOverload* pymeth, CPPInstance* pyobj, PyObje // e.g. class methods (C++ static); notify downstream to expect a 'self' newPyMeth->fFlags |= CallContext::kFromDescr; -#else -// new method is to be bound to current object - Py_INCREF((PyObject*)pyobj); - newPyMeth->fSelf = pyobj; - -// reset flags of the new method, as there is a self (which may or may not have -// come in through direct call syntax, but that's now impossible to know, so this -// is the safer choice) - newPyMeth->fFlags = CallContext::kNone; -#endif - if (gc_track) PyObject_GC_Track(newPyMeth); @@ -1045,11 +921,7 @@ PyTypeObject CPPOverload_Type = { sizeof(CPPOverload), // tp_basicsize 0, // tp_itemsize (destructor)mp_dealloc, // tp_dealloc -#if PY_VERSION_HEX >= 0x03080000 offsetof(CPPOverload, fVectorCall), -#else - 0, // tp_vectorcall_offset / tp_print -#endif 0, // tp_getattr 0, // tp_setattr 0, // tp_as_async / tp_compare @@ -1058,20 +930,13 @@ PyTypeObject CPPOverload_Type = { 0, // tp_as_sequence 0, // tp_as_mapping (hashfunc)mp_hash, // tp_hash -#if PY_VERSION_HEX >= 0x03080000 (ternaryfunc)PyVectorcall_Call, // tp_call -#else - (ternaryfunc)mp_call, // tp_call -#endif (reprfunc)mp_str, // tp_str 0, // tp_getattro 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC -#if PY_VERSION_HEX >= 0x03080000 - | Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_METHOD_DESCRIPTOR -#endif - , // tp_flags + | Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_METHOD_DESCRIPTOR, // tp_flags (char*)"cppyy method proxy (internal)", // tp_doc (traverseproc)mp_traverse, // tp_traverse (inquiry)mp_clear, // tp_clear @@ -1096,25 +961,12 @@ PyTypeObject CPPOverload_Type = { 0, // tp_mro 0, // tp_cache 0, // tp_subclasses - 0 // tp_weaklist -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, // tp_weaklist + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; } // namespace CPyCppyy @@ -1137,9 +989,7 @@ void CPyCppyy::CPPOverload::Set(const std::string& name, std::vectorfFlags |= CallContext::kIsCreator; -#if PY_VERSION_HEX >= 0x03080000 fVectorCall = (vectorcallfunc)mp_vectorcall; -#endif } //---------------------------------------------------------------------------- diff --git a/src/CPyCppyy/src/CPPOverload.h b/src/CPyCppyy/src/CPPOverload.h index 8bd3b2d..65c54b4 100644 --- a/src/CPyCppyy/src/CPPOverload.h +++ b/src/CPyCppyy/src/CPPOverload.h @@ -78,9 +78,7 @@ class CPPOverload { CPPInstance* fSelf; // must be first (same layout as TemplateProxy) MethodInfo_t* fMethodInfo; uint32_t fFlags; -#if PY_VERSION_HEX >= 0x03080000 vectorcallfunc fVectorCall; -#endif private: CPPOverload() = delete; diff --git a/src/CPyCppyy/src/CPPScope.cxx b/src/CPyCppyy/src/CPPScope.cxx index af40745..841f4bf 100644 --- a/src/CPyCppyy/src/CPPScope.cxx +++ b/src/CPyCppyy/src/CPPScope.cxx @@ -5,6 +5,7 @@ #include "CPPEnum.h" #include "CPPFunction.h" #include "CPPOverload.h" +#include "Cppyy.h" #include "CustomPyTypes.h" #include "Dispatcher.h" #include "ProxyWrappers.h" @@ -202,8 +203,14 @@ static PyObject* pt_new(PyTypeObject* subtype, PyObject* args, PyObject* kwds) subtype->tp_dealloc = (destructor)meta_dealloc; // creation of the python-side class; extend the size if this is a smart ptr - Cppyy::TCppType_t raw{0}; Cppyy::TCppMethod_t deref{0}; - if (CPPScope_CheckExact(subtype)) { +// +// A namespace can never be a smart pointer, so skip the check for namespaces. +// This matters beyond performance: GetSmartPtrInfo() resolves the scope name +// through a slow interpreter lookup that triggers autoloading of the library +// providing the scope, which we don't want to do unnecessarily. + Cppyy::TCppScope_t raw; + Cppyy::TCppMethod_t deref; + if (CPPScope_CheckExact(subtype) && !Cppyy::IsNamespace(((CPPScope*)subtype)->fCppType)) { if (Cppyy::GetSmartPtrInfo(Cppyy::GetScopedFinalName(((CPPScope*)subtype)->fCppType), &raw, &deref)) subtype->tp_basicsize = sizeof(CPPSmartClass); } @@ -274,7 +281,7 @@ 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("exception", Cppyy::GetScope("std")); + static Cppyy::TCppScope_t exc_type = Cppyy::GetScope("exception", Cppyy::GetScope("std")); if (Cppyy::IsSubclass(result->fCppType, exc_type)) result->fFlags |= CPPScope::kIsException; if (!(result->fFlags & CPPScope::kIsPython)) @@ -365,7 +372,7 @@ static PyObject* meta_getattro(PyObject* pyclass, PyObject* pyname) if ((klass->fFlags & CPPScope::kIsNamespace) || scope == Cppyy::GetGlobalScope()) { // tickle lazy lookup of functions - const std::vector methods = + const std::vector methods = Cppyy::GetMethodsFromName(scope, name); if (!methods.empty()) { // function exists, now collect overloads @@ -663,11 +670,7 @@ PyTypeObject CPPScope_Type = { (getattrofunc)meta_getattro, // tp_getattro (setattrofunc)meta_setattro, // tp_setattro 0, // tp_as_buffer - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE -#if PY_VERSION_HEX >= 0x03040000 - | Py_TPFLAGS_TYPE_SUBCLASS -#endif - , // tp_flags + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TYPE_SUBCLASS, // tp_flags (char*)"CPyCppyy metatype (internal)", // tp_doc 0, // tp_traverse 0, // tp_clear @@ -692,25 +695,12 @@ PyTypeObject CPPScope_Type = { 0, // tp_mro 0, // tp_cache 0, // tp_subclasses - 0 // tp_weaklist -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, // tp_weaklist + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; } // namespace CPyCppyy diff --git a/src/CPyCppyy/src/CPPScope.h b/src/CPyCppyy/src/CPPScope.h index 370224a..355dfae 100644 --- a/src/CPyCppyy/src/CPPScope.h +++ b/src/CPyCppyy/src/CPPScope.h @@ -1,6 +1,10 @@ #ifndef CPYCPPYY_CPPSCOPE_H #define CPYCPPYY_CPPSCOPE_H +#include "Python.h" +#include "Cppyy.h" +#include + #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 2 // In p2.2, PyHeapTypeObject is not yet part of the interface @@ -19,6 +23,7 @@ typedef struct { #endif // Standard +#include #include @@ -31,7 +36,7 @@ namespace CPyCppyy { @version 2.0 */ -typedef std::map CppToPyMap_t; +typedef std::unordered_map CppToPyMap_t; namespace Utility { struct PyOperators; } class CPPScope { diff --git a/src/CPyCppyy/src/CPyCppyy.h b/src/CPyCppyy/src/CPyCppyy.h index 84b193d..8cf3e35 100644 --- a/src/CPyCppyy/src/CPyCppyy.h +++ b/src/CPyCppyy/src/CPyCppyy.h @@ -39,96 +39,22 @@ namespace CPyCppyy { typedef Py_ssize_t dim_t; } // namespace CPyCppyy -// for 3.3 support -#if PY_VERSION_HEX < 0x03030000 - typedef PyDictEntry* (*dict_lookup_func)(PyDictObject*, PyObject*, long); -#else #if PY_VERSION_HEX >= 0x030b0000 typedef Py_ssize_t (*dict_lookup_func)( PyDictObject*, PyObject*, Py_hash_t, PyObject**); -#elif PY_VERSION_HEX >= 0x03060000 +#else typedef Py_ssize_t (*dict_lookup_func)( PyDictObject*, PyObject*, Py_hash_t, PyObject***, Py_ssize_t*); -#else - struct PyDictKeyEntry; - typedef PyDictKeyEntry* (*dict_lookup_func)(PyDictObject*, PyObject*, Py_hash_t, PyObject***); -#define PyDictEntry PyDictKeyEntry #endif -#endif - -// for 3.0 support (backwards compatibility, really) -#if PY_VERSION_HEX < 0x03000000 -#define PyBytes_Check PyString_Check -#define PyBytes_CheckExact PyString_CheckExact -#define PyBytes_AS_STRING PyString_AS_STRING -#define PyBytes_AsString PyString_AsString -#define PyBytes_AsStringAndSize PyString_AsStringAndSize -#define PyBytes_GET_SIZE PyString_GET_SIZE -#define PyBytes_Size PyString_Size -#define PyBytes_FromFormat PyString_FromFormat -#define PyBytes_FromString PyString_FromString -#define PyBytes_FromStringAndSize PyString_FromStringAndSize - -#define PyBytes_Type PyString_Type - -#define CPyCppyy_PyText_Check PyString_Check -#define CPyCppyy_PyText_CheckExact PyString_CheckExact -#define CPyCppyy_PyText_AsString PyString_AS_STRING -#define CPyCppyy_PyText_AsStringChecked PyString_AsString -#define CPyCppyy_PyText_GET_SIZE PyString_GET_SIZE -#define CPyCppyy_PyText_GetSize PyString_Size -#define CPyCppyy_PyText_FromFormat PyString_FromFormat -#define CPyCppyy_PyText_FromString PyString_FromString -#define CPyCppyy_PyText_InternFromString PyString_InternFromString -#define CPyCppyy_PyText_Append PyString_Concat -#define CPyCppyy_PyText_AppendAndDel PyString_ConcatAndDel -#define CPyCppyy_PyText_FromStringAndSize PyString_FromStringAndSize - -static inline const char* CPyCppyy_PyText_AsStringAndSize(PyObject* pystr, Py_ssize_t* size) -{ - const char* cstr = CPyCppyy_PyText_AsStringChecked(pystr); - if (cstr) *size = CPyCppyy_PyText_GetSize(pystr); - return cstr; -} - -#define CPyCppyy_PyText_Type PyString_Type - -#define CPyCppyy_PyUnicode_GET_SIZE PyUnicode_GET_SIZE - -static inline PyObject* CPyCppyy_PyCapsule_New( - void* cobj, const char* /* name */, void (*destr)(void*)) -{ - return PyCObject_FromVoidPtr(cobj, destr); -} -#define CPyCppyy_PyCapsule_CheckExact PyCObject_Check -static inline void* CPyCppyy_PyCapsule_GetPointer(PyObject* capsule, const char* /* name */) -{ - return (void*)PyCObject_AsVoidPtr(capsule); -} - -#define CPPYY__long__ "__long__" -#define CPPYY__idiv__ "__idiv__" -#define CPPYY__div__ "__div__" -#define CPPYY__next__ "next" - -typedef long Py_hash_t; - -#endif // ! 3.0 // for 3.0 support (backwards compatibility, really) -#if PY_VERSION_HEX >= 0x03000000 #define CPyCppyy_PyText_Check PyUnicode_Check #define CPyCppyy_PyText_CheckExact PyUnicode_CheckExact #define CPyCppyy_PyText_AsString PyUnicode_AsUTF8 #define CPyCppyy_PyText_AsStringChecked PyUnicode_AsUTF8 #define CPyCppyy_PyText_GetSize PyUnicode_GetSize -#if PY_VERSION_HEX < 0x03090000 -#define CPyCppyy_PyText_GET_SIZE PyUnicode_GET_SIZE -#define CPyCppyy_PyUnicode_GET_SIZE PyUnicode_GET_LENGTH -#else #define CPyCppyy_PyText_GET_SIZE PyUnicode_GET_LENGTH #define CPyCppyy_PyUnicode_GET_SIZE PyUnicode_GET_LENGTH -#endif #define CPyCppyy_PyText_FromFormat PyUnicode_FromFormat #define CPyCppyy_PyText_FromString PyUnicode_FromString #define CPyCppyy_PyText_InternFromString PyUnicode_InternFromString @@ -136,11 +62,7 @@ typedef long Py_hash_t; #define CPyCppyy_PyText_AppendAndDel PyUnicode_AppendAndDel #define CPyCppyy_PyText_FromStringAndSize PyUnicode_FromStringAndSize -#if PY_VERSION_HEX >= 0x03030000 #define _CPyCppyy_PyText_AsStringAndSize PyUnicode_AsUTF8AndSize -#else -#define _CPyCppyy_PyText_AsStringAndSize PyUnicode_AsStringAndSize -#endif // >= 3.3 static inline const char* CPyCppyy_PyText_AsStringAndSize(PyObject* pystr, Py_ssize_t* size) { @@ -180,88 +102,24 @@ static inline const char* CPyCppyy_PyText_AsStringAndSize(PyObject* pystr, Py_ss #define PyClass_Check PyType_Check #define PyBuffer_Type PyMemoryView_Type -#endif // ! 3.0 -#if PY_VERSION_HEX >= 0x03020000 #define CPyCppyy_PySliceCast PyObject* #define PyUnicode_GetSize PyUnicode_GetLength -#else -#define CPyCppyy_PySliceCast PySliceObject* -#endif // >= 3.2 - -// feature of 3.0 not in 2.5 and earlier -#if PY_VERSION_HEX < 0x02060000 -#define PyVarObject_HEAD_INIT(type, size) \ - PyObject_HEAD_INIT(type) size, -#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) -#endif -// API changes in 2.5 (int -> Py_ssize_t) and 3.2 (PyUnicodeObject -> PyObject) -#if PY_VERSION_HEX < 0x03020000 -static inline Py_ssize_t CPyCppyy_PyUnicode_AsWideChar(PyObject* pyobj, wchar_t* w, Py_ssize_t size) -{ -#if PY_VERSION_HEX < 0x02050000 - return (Py_ssize_t)PyUnicode_AsWideChar((PyUnicodeObject*)pyobj, w, (int)size); -#else - return PyUnicode_AsWideChar((PyUnicodeObject*)pyobj, w, size); -#endif -} -#else #define CPyCppyy_PyUnicode_AsWideChar PyUnicode_AsWideChar -#endif -// backwards compatibility, pre python 2.5 -#if PY_VERSION_HEX < 0x02050000 -typedef int Py_ssize_t; -#define PyInt_AsSsize_t PyInt_AsLong -#define PyInt_FromSsize_t PyInt_FromLong -# define PY_SSIZE_T_FORMAT "%d" -# if !defined(PY_SSIZE_T_MIN) -# define PY_SSIZE_T_MAX INT_MAX -# define PY_SSIZE_T_MIN INT_MIN +#ifdef R__MACOSX +# if SIZEOF_SIZE_T == SIZEOF_INT +# if defined(MAC_OS_X_VERSION_10_4) +# define PY_SSIZE_T_FORMAT "%ld" +# else +# define PY_SSIZE_T_FORMAT "%d" +# endif +# elif SIZEOF_SIZE_T == SIZEOF_LONG +# define PY_SSIZE_T_FORMAT "%ld" # endif -#define ssizeobjargproc intobjargproc -#define lenfunc inquiry -#define ssizeargfunc intargfunc - -#define PyIndex_Check(obj) \ - (PyInt_Check(obj) || PyLong_Check(obj)) - -inline Py_ssize_t PyNumber_AsSsize_t(PyObject* obj, PyObject*) { - return (Py_ssize_t)PyLong_AsLong(obj); -} - #else -# ifdef R__MACOSX -# if SIZEOF_SIZE_T == SIZEOF_INT -# if defined(MAC_OS_X_VERSION_10_4) -# define PY_SSIZE_T_FORMAT "%ld" -# else -# define PY_SSIZE_T_FORMAT "%d" -# endif -# elif SIZEOF_SIZE_T == SIZEOF_LONG -# define PY_SSIZE_T_FORMAT "%ld" -# endif -# else -# define PY_SSIZE_T_FORMAT "%zd" -# endif -#endif - -#if PY_VERSION_HEX < 0x02020000 -#define PyBool_FromLong PyInt_FromLong -#endif - -#if PY_VERSION_HEX < 0x03000000 -// the following should quiet Solaris -#ifdef Py_False -#undef Py_False -#define Py_False ((PyObject*)(void*)&_Py_ZeroStruct) -#endif - -#ifdef Py_True -#undef Py_True -#define Py_True ((PyObject*)(void*)&_Py_TrueStruct) -#endif +# define PY_SSIZE_T_FORMAT "%zd" #endif #ifndef Py_RETURN_NONE @@ -277,14 +135,9 @@ inline Py_ssize_t PyNumber_AsSsize_t(PyObject* obj, PyObject*) { #endif // vector call support -#if PY_VERSION_HEX >= 0x03090000 #define CPyCppyy_PyCFunction_Call PyObject_Call -#else -#define CPyCppyy_PyCFunction_Call PyCFunction_Call -#endif // vector call support -#if PY_VERSION_HEX >= 0x03080000 typedef PyObject* const* CPyCppyy_PyArgs_t; static inline PyObject* CPyCppyy_PyArgs_GET_ITEM(CPyCppyy_PyArgs_t args, Py_ssize_t i) { return args[i]; @@ -301,11 +154,7 @@ static inline CPyCppyy_PyArgs_t CPyCppyy_PyArgs_New(Py_ssize_t N) { static inline void CPyCppyy_PyArgs_DEL(CPyCppyy_PyArgs_t args) { PyMem_Free((void*)args); } -#if PY_VERSION_HEX >= 0x03090000 #define CPyCppyy_PyObject_Call PyObject_Vectorcall -#else -#define CPyCppyy_PyObject_Call _PyObject_Vectorcall -#endif inline PyObject* CPyCppyy_tp_call( PyObject* cb, CPyCppyy_PyArgs_t args, size_t nargsf, PyObject* kwds) { Py_ssize_t offset = Py_TYPE(cb)->tp_vectorcall_offset; @@ -317,32 +166,6 @@ inline PyObject* CPyCppyy_tp_call( #define Py_TPFLAGS_HAVE_VECTORCALL _Py_TPFLAGS_HAVE_VECTORCALL #endif -#else - -typedef PyObject* CPyCppyy_PyArgs_t; -static inline PyObject* CPyCppyy_PyArgs_GET_ITEM(CPyCppyy_PyArgs_t args, Py_ssize_t i) { - return PyTuple_GET_ITEM(args, i); -} -static inline PyObject* CPyCppyy_PyArgs_SET_ITEM(CPyCppyy_PyArgs_t args, Py_ssize_t i, PyObject* item) { - return PyTuple_SET_ITEM(args, i, item); -} -static inline Py_ssize_t CPyCppyy_PyArgs_GET_SIZE(CPyCppyy_PyArgs_t args, size_t) { - return PyTuple_GET_SIZE(args); -} -static inline CPyCppyy_PyArgs_t CPyCppyy_PyArgs_New(Py_ssize_t N) { - return PyTuple_New(N); -} -static inline void CPyCppyy_PyArgs_DEL(CPyCppyy_PyArgs_t args) { - Py_DECREF(args); -} -inline PyObject* CPyCppyy_PyObject_Call(PyObject* cb, PyObject* args, size_t, PyObject* kwds) { - return PyObject_Call(cb, args, kwds); -} -inline PyObject* CPyCppyy_tp_call(PyObject* cb, PyObject* args, size_t, PyObject* kwds) { - return Py_TYPE(cb)->tp_call(cb, args, kwds); -} -#endif - // weakref forced strong reference #if PY_VERSION_HEX < 0x30d0000 static inline PyObject* CPyCppyy_GetWeakRef(PyObject* ref) { @@ -361,28 +184,40 @@ static inline PyObject* CPyCppyy_GetWeakRef(PyObject* ref) { } #endif -// Py_TYPE as inline function -#if PY_VERSION_HEX < 0x03090000 && !defined(Py_SET_TYPE) -static inline -void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) { ob->ob_type = type; } -#define Py_SET_TYPE(ob, type) _Py_SET_TYPE((PyObject*)(ob), type) -#endif - -// py39 gained several faster (through vector call) equivalent method call API -#if PY_VERSION_HEX < 0x03090000 -static inline PyObject* PyObject_CallMethodNoArgs(PyObject* obj, PyObject* name) { - return PyObject_CallMethodObjArgs(obj, name, nullptr); -} - -static inline PyObject* PyObject_CallMethodOneArg(PyObject* obj, PyObject* name, PyObject* arg) { - return PyObject_CallMethodObjArgs(obj, name, arg, nullptr); -} -#endif - // C++ version of the cppyy API #include "Cppyy.h" // export macros for our own API #include "CPyCppyy/CommonDefs.h" +// --- reusable PyTypeObject initializer tail ------------------------------- +// Members appended to PyTypeObject in newer CPython releases. Every CPyCppyy +// type leaves all of these zero/null-initialized, so they share one tail. +// To support a future Python version, add one block below and one line to +// CPYCPPYY_PYTYPE_TAIL. + +#if PY_VERSION_HEX >= 0x030c0000 +#define CPYCPPYY_TP_WATCHED , 0 /* tp_watched (>= 3.12) */ +#else +#define CPYCPPYY_TP_WATCHED +#endif + +#if PY_VERSION_HEX >= 0x030d0000 +#define CPYCPPYY_TP_VERSIONS_USED , 0 /* tp_versions_used(>= 3.13) */ +#else +#define CPYCPPYY_TP_VERSIONS_USED +#endif + +#if PY_VERSION_HEX >= 0x030f0000 +#define CPYCPPYY_TP_ITERITEM , nullptr /* _tp_iteritem (>= 3.15) */ +#else +#define CPYCPPYY_TP_ITERITEM +#endif + +#define CPYCPPYY_PYTYPE_TAIL \ + CPYCPPYY_TP_WATCHED \ + CPYCPPYY_TP_VERSIONS_USED \ + CPYCPPYY_TP_ITERITEM +// -------------------------------------------------------------------------- + #endif // !CPYCPPYY_CPYCPPYY_H diff --git a/src/CPyCppyy/src/CPyCppyyModule.cxx b/src/CPyCppyy/src/CPyCppyyModule.cxx index 9688dcb..a6b4441 100644 --- a/src/CPyCppyy/src/CPyCppyyModule.cxx +++ b/src/CPyCppyy/src/CPyCppyyModule.cxx @@ -7,6 +7,7 @@ #include "CPPInstance.h" #include "CPPOverload.h" #include "CPPScope.h" +#include "Cppyy.h" #include "CustomPyTypes.h" #include "LowLevelViews.h" #include "MemoryRegulator.h" @@ -15,6 +16,7 @@ #include "TemplateProxy.h" #include "TupleOfInstances.h" #include "Utility.h" +#include #define CPYCPPYY_INTERNAL 1 #include "CPyCppyy/DispatchPtr.h" @@ -28,7 +30,7 @@ PyObject* Instance_FromVoidPtr( // Standard #include #include -#include +#include #include #include #include @@ -42,7 +44,7 @@ dict_lookup_func gDictLookupOrg = nullptr; } // namespace CPyCppyy #endif -std::map TypeReductionMap; +std::unordered_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 *" @@ -50,7 +52,6 @@ std::map TypeReductionMap; #if PY_VERSION_HEX < 0x030b0000 //- from Python's dictobject.c ------------------------------------------------- -#if PY_VERSION_HEX >= 0x03030000 typedef struct PyDictKeyEntry { /* Cached hash code of me_key. */ Py_hash_t me_hash; @@ -63,7 +64,6 @@ std::map TypeReductionMap; Py_ssize_t dk_size; dict_lookup_func dk_lookup; Py_ssize_t dk_usable; -#if PY_VERSION_HEX >= 0x03060000 Py_ssize_t dk_nentries; union { int8_t as_1[8]; @@ -73,21 +73,11 @@ std::map TypeReductionMap; int64_t as_8[1]; #endif } dk_indices; -#else - PyDictKeyEntry dk_entries[1]; -#endif } PyDictKeysObject; #define CPYCPPYY_GET_DICT_LOOKUP(mp) \ ((dict_lookup_func&)mp->ma_keys->dk_lookup) -#else - -#define CPYCPPYY_GET_DICT_LOOKUP(mp) \ - ((dict_lookup_func&)mp->ma_lookup) - -#endif - #endif // PY_VERSION_HEX < 0x030b0000 //- data ----------------------------------------------------------------------- @@ -108,40 +98,18 @@ static int nullptr_nonzero(PyObject*) static PyNumberMethods nullptr_as_number = { 0, 0, 0, -#if PY_VERSION_HEX < 0x03000000 - 0, -#endif 0, 0, 0, 0, 0, 0, (inquiry)nullptr_nonzero, // tp_nonzero (nb_bool in p3) 0, 0, 0, 0, 0, 0, -#if PY_VERSION_HEX < 0x03000000 - 0, // nb_coerce -#endif 0, 0, 0, -#if PY_VERSION_HEX < 0x03000000 - 0, 0, -#endif 0, 0, 0, -#if PY_VERSION_HEX < 0x03000000 - 0, // nb_inplace_divide -#endif - 0, 0, 0, 0, 0, 0, 0 -#if PY_VERSION_HEX >= 0x02020000 - , 0 // nb_floor_divide -#if PY_VERSION_HEX < 0x03000000 - , 0 // nb_true_divide -#else - , 0 // nb_true_divide -#endif - , 0, 0 -#endif -#if PY_VERSION_HEX >= 0x02050000 - , 0 // nb_index -#endif -#if PY_VERSION_HEX >= 0x03050000 - , 0 // nb_matrix_multiply - , 0 // nb_inplace_matrix_multiply -#endif + 0, 0, 0, 0, 0, 0, 0, + 0, // nb_floor_divide + 0, // nb_true_divide + 0, 0, + 0, // nb_index + 0, // nb_matrix_multiply + 0 // nb_inplace_matrix_multiply }; static PyTypeObject PyNullPtr_t_Type = { @@ -160,25 +128,12 @@ static PyTypeObject PyNullPtr_t_Type = { (hashfunc)_Py_HashPointer, // tp_hash #endif 0, 0, 0, 0, 0, Py_TPFLAGS_DEFAULT, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; @@ -207,25 +162,12 @@ static PyTypeObject PyDefault_t_Type = { (hashfunc)_Py_HashPointer, // tp_hash #endif 0, 0, 0, 0, 0, Py_TPFLAGS_DEFAULT, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; namespace { @@ -256,13 +198,13 @@ namespace CPyCppyy { PyObject* gSegvException = nullptr; PyObject* gIllException = nullptr; PyObject* gAbrtException = nullptr; - std::set gPinnedTypes; + std::unordered_set gPinnedTypes; std::ostringstream gCapturedError; std::streambuf* gOldErrorBuffer = nullptr; - std::map> &pythonizations() + std::unordered_map> &pythonizations() { - static std::map> pyzMap; + static std::unordered_map> pyzMap; return pyzMap; } } @@ -294,7 +236,6 @@ class GblGetter { } // unnamed namespace -#if PY_VERSION_HEX >= 0x03060000 inline Py_ssize_t OrgDictLookup(PyDictObject* mp, PyObject* key, Py_hash_t hash, PyObject*** value_addr, Py_ssize_t* hashpos) { @@ -305,50 +246,16 @@ inline Py_ssize_t OrgDictLookup(PyDictObject* mp, PyObject* key, Py_ssize_t CPyCppyyLookDictString(PyDictObject* mp, PyObject* key, Py_hash_t hash, PyObject*** value_addr, Py_ssize_t* hashpos) - -#elif PY_VERSION_HEX >= 0x03030000 -inline PyDictKeyEntry* OrgDictLookup( - PyDictObject* mp, PyObject* key, Py_hash_t hash, PyObject*** value_addr) -{ - return (*gDictLookupOrg)(mp, key, hash, value_addr); -} - -#define CPYCPPYY_ORGDICT_LOOKUP(mp, key, hash, value_addr, hashpos) \ - OrgDictLookup(mp, key, hash, value_addr) - -PyDictKeyEntry* CPyCppyyLookDictString( - PyDictObject* mp, PyObject* key, Py_hash_t hash, PyObject*** value_addr) - -#else /* < 3.3 */ - -inline PyDictEntry* OrgDictLookup(PyDictObject* mp, PyObject* key, long hash) -{ - return (*gDictLookupOrg)(mp, key, hash); -} - -#define CPYCPPYY_ORGDICT_LOOKUP(mp, key, hash, value_addr, hashpos) \ - OrgDictLookup(mp, key, hash) - -PyDictEntry* CPyCppyyLookDictString(PyDictObject* mp, PyObject* key, long hash) -#endif { static GblGetter gbl; -#if PY_VERSION_HEX >= 0x03060000 Py_ssize_t ep; -#else - PyDictEntry* ep; -#endif // first search dictionary itself ep = CPYCPPYY_ORGDICT_LOOKUP(mp, key, hash, value_addr, hashpos); if (gDictLookupActive) return ep; -#if PY_VERSION_HEX >= 0x03060000 if (ep >= 0) -#else - if (!ep || (ep->me_key && ep->me_value)) -#endif return ep; // filter for builtins @@ -379,12 +286,7 @@ PyDictEntry* CPyCppyyLookDictString(PyDictObject* mp, PyObject* key, long hash) if (PyDict_SetItem((PyObject*)mp, key, val) == 0) { ep = CPYCPPYY_ORGDICT_LOOKUP(mp, key, hash, value_addr, hashpos); } else { -#if PY_VERSION_HEX >= 0x03060000 ep = -1; -#else - ep->me_key = nullptr; - ep->me_value = nullptr; -#endif } CPYCPPYY_GET_DICT_LOOKUP(mp) = CPyCppyyLookDictString; // restore @@ -393,7 +295,6 @@ PyDictEntry* CPyCppyyLookDictString(PyDictObject* mp, PyObject* key, long hash) } else PyErr_Clear(); -#if PY_VERSION_HEX >= 0x03030000 if (mp->ma_keys->dk_usable <= 0) { // big risk that this lookup will result in a resize, so force it here // to be able to reset the lookup function; of course, this is nowhere @@ -421,7 +322,6 @@ PyDictEntry* CPyCppyyLookDictString(PyDictObject* mp, PyObject* key, long hash) gDictLookupOrg = CPYCPPYY_GET_DICT_LOOKUP(mp); CPYCPPYY_GET_DICT_LOOKUP(mp) = CPyCppyyLookDictString; // restore } -#endif // stopped calling into the reflection system gDictLookupActive = false; @@ -622,11 +522,7 @@ static PyObject* AsCapsule(PyObject* /* unused */, PyObject* args, PyObject* kwd // Return object proxy as an opaque PyCapsule. void* addr = GetCPPInstanceAddress("as_capsule", args, kwds); if (addr) -#if PY_VERSION_HEX < 0x02060000 - return PyCObject_FromVoidPtr(addr, nullptr); -#else return PyCapsule_New(addr, nullptr, nullptr); -#endif return nullptr; } @@ -666,7 +562,7 @@ static PyObject* AsMemoryView(PyObject* /* unused */, PyObject* pyobject) } CPPInstance* pyobj = (CPPInstance*)pyobject; - Cppyy::TCppType_t klass = ((CPPClass*)Py_TYPE(pyobject))->fCppType; + Cppyy::TCppScope_t klass = ((CPPClass*)Py_TYPE(pyobject))->fCppType; Py_ssize_t array_len = pyobj->ArrayLength(); @@ -705,7 +601,7 @@ static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) } // convert 2nd argument first (used for both pointer value and instance cases) - Cppyy::TCppScope_t cast_type = 0; + Cppyy::TCppScope_t cast_type = nullptr; PyObject* arg1 = PyTuple_GET_ITEM(args, 1); if (!CPyCppyy_PyText_Check(arg1)) { // not string, then class if (CPPScope_Check(arg1)) @@ -744,7 +640,7 @@ static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) } int direction = 0; - Cppyy::TCppScope_t base = 0, derived = 0; + Cppyy::TCppScope_t base = nullptr, derived = nullptr; if (Cppyy::IsSubclass(cast_type, cur_type)) { derived = cast_type; base = cur_type; @@ -774,14 +670,14 @@ static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds) if (!isPython) { // ordinary C++ class PyObject* pyobj = BindCppObjectNoCast( - (void*)((intptr_t)address + offset), cast_type, owns ? CPPInstance::kIsOwner : 0); + Cppyy::TCppObject_t((void*)((intptr_t)address.data + offset)), cast_type, owns ? CPPInstance::kIsOwner : 0); if (owns && pyobj) arg0_pyobj->CppOwns(); return pyobj; } else { // rebinding to a Python-side class, create a fresh instance first to be able to // perform a lookup of the original dispatch object and if found, return original - void* cast_address = (void*)((intptr_t)address + offset); + void* cast_address = (void*)((intptr_t)address.data + offset); PyObject* pyobj = ((PyTypeObject*)arg1)->tp_new((PyTypeObject*)arg1, nullptr, nullptr); ((CPPInstance*)pyobj)->GetObjectRaw() = cast_address; @@ -988,7 +884,7 @@ static PyObject* AddSmartPtrType(PyObject*, PyObject* args) if (!PyArg_ParseTuple(args, const_cast("s"), &type_name)) return nullptr; - Cppyy::AddSmartPtrType(type_name); + // Cppyy::AddSmartPtrType(type_name); Py_RETURN_NONE; } @@ -1067,7 +963,6 @@ static PyMethodDef gCPyCppyyMethods[] = { }; -#if PY_VERSION_HEX >= 0x03000000 struct module_state { PyObject *error; }; @@ -1099,8 +994,6 @@ static struct PyModuleDef moduledef = { nullptr }; -#endif - namespace CPyCppyy { //---------------------------------------------------------------------------- @@ -1113,9 +1006,6 @@ extern "C" PyObject* PyInit_libcppyy() return nullptr; // setup interpreter -#if PY_VERSION_HEX < 0x03090000 - PyEval_InitThreads(); -#endif #if PY_VERSION_HEX < 0x030b0000 // prepare for laziness (the insert is needed to capture the most generic lookup @@ -1124,20 +1014,12 @@ extern "C" PyObject* PyInit_libcppyy() PyObject* notstring = PyInt_FromLong(5); PyDict_SetItem(dict, notstring, notstring); Py_DECREF(notstring); -#if PY_VERSION_HEX >= 0x03030000 gDictLookupOrg = (dict_lookup_func)((PyDictObject*)dict)->ma_keys->dk_lookup; -#else - gDictLookupOrg = (dict_lookup_func)((PyDictObject*)dict)->ma_lookup; -#endif Py_DECREF(dict); #endif // PY_VERSION_HEX < 0x030b0000 // setup this module -#if PY_VERSION_HEX >= 0x03000000 gThisModule = PyModule_Create(&moduledef); -#else - gThisModule = Py_InitModule(const_cast("libcppyy"), gCPyCppyyMethods); -#endif if (!gThisModule) return nullptr; @@ -1175,15 +1057,6 @@ extern "C" PyObject* PyInit_libcppyy() if (!Utility::InitProxy(gThisModule, &CPPDataMember_Type, "CPPDataMember")) return nullptr; -// inject custom data types -#if PY_VERSION_HEX < 0x03000000 - if (!Utility::InitProxy(gThisModule, &RefFloat_Type, "Double")) - return nullptr; - - if (!Utility::InitProxy(gThisModule, &RefInt_Type, "Long")) - return nullptr; -#endif - if (!Utility::InitProxy(gThisModule, &CustomInstanceMethod_Type, "InstanceMethod")) return nullptr; diff --git a/src/CPyCppyy/src/CallContext.h b/src/CPyCppyy/src/CallContext.h index 98cb156..f093959 100644 --- a/src/CPyCppyy/src/CallContext.h +++ b/src/CPyCppyy/src/CallContext.h @@ -1,8 +1,12 @@ #ifndef CPYCPPYY_CALLCONTEXT_H #define CPYCPPYY_CALLCONTEXT_H +#include "Python.h" +#include "Cppyy.h" + // Standard #include +#include #include @@ -46,7 +50,7 @@ struct Parameter { // extra call information struct CallContext { - CallContext() : fCurScope(0), fPyContext(nullptr), fFlags(0), + CallContext() : fCurScope(nullptr), fPyContext(nullptr), fFlags(0), fArgsVec(nullptr), fNArgs(0), fTemps(nullptr) {} CallContext(const CallContext&) = delete; CallContext& operator=(const CallContext&) = delete; diff --git a/src/CPyCppyy/src/Converters.cxx b/src/CPyCppyy/src/Converters.cxx index d037803..c302129 100644 --- a/src/CPyCppyy/src/Converters.cxx +++ b/src/CPyCppyy/src/Converters.cxx @@ -1,5 +1,6 @@ // Bindings #include "CPyCppyy.h" +#include "Cppyy.h" #include "DeclareConverters.h" #include "CallContext.h" #include "CPPExcInstance.h" @@ -33,27 +34,12 @@ #include #endif -// codecvt does not exist for gcc4.8.5 and is in principle deprecated; it is -// only used in py2 for char -> wchar_t conversion for std::wstring; if not -// available, the conversion is done through Python (requires an extra copy) -#if PY_VERSION_HEX < 0x03000000 -#if defined(__GNUC__) && !defined(__APPLE__) -# if __GNUC__ > 4 && __has_include("codecvt") -# include -# define HAS_CODECVT 1 -# endif -#else -#include -#define HAS_CODECVT 1 -#endif -#endif // py2 - //- data _____________________________________________________________________ namespace CPyCppyy { // factories - typedef std::map ConvFactories_t; + typedef std::unordered_map ConvFactories_t; static ConvFactories_t gConvFactories; // special objects @@ -67,18 +53,7 @@ namespace CPyCppyy { // 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 -// p3 has at least 2 ref-counts, as contrary to p2, it will create a descriptor -// copy for the method holding self in the case of __init__; but there can also -// be a reference held by the frame object, which is indistinguishable from a -// local variable reference, so the cut-off has to remain 2. -const Py_ssize_t MOVE_REFCOUNT_CUTOFF = 2; -#else -// 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; } @@ -320,7 +295,7 @@ static bool HasLifeLine(PyObject* holder, intptr_t ref) //- helper to work with both CPPInstance and CPPExcInstance ------------------ static inline CPyCppyy::CPPInstance* GetCppInstance( - PyObject* pyobject, Cppyy::TCppType_t klass = (Cppyy::TCppType_t)0, bool accept_rvalue = false) + PyObject* pyobject, Cppyy::TCppScope_t klass = Cppyy::TCppScope_t{}, bool accept_rvalue = false) { using namespace CPyCppyy; if (CPPInstance_Check(pyobject)) @@ -486,7 +461,7 @@ static inline bool CArraySetArg( //- helper for implicit conversions ------------------------------------------ -static inline CPyCppyy::CPPInstance* ConvertImplicit(Cppyy::TCppType_t klass, +static inline CPyCppyy::CPPInstance* ConvertImplicit(Cppyy::TCppScope_t klass, PyObject* pyobject, CPyCppyy::Parameter& para, CPyCppyy::CallContext* ctxt, bool manage=true) { using namespace CPyCppyy; @@ -790,13 +765,6 @@ bool CPyCppyy::LongRefConverter::SetArg( PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) { // convert to C++ long&, set arg for call -#if PY_VERSION_HEX < 0x03000000 - if (RefInt_CheckExact(pyobject)) { - para.fValue.fVoidp = (void*)&((PyIntObject*)pyobject)->ob_ival; - para.fTypeCode = 'V'; - return true; - } -#endif if (Py_TYPE(pyobject) == GetCTypesType(ct_c_long)) { para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr; @@ -834,21 +802,12 @@ bool CPyCppyy::IntRefConverter::SetArg( PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) { // convert to C++ (pseudo)int&, set arg for call -#if PY_VERSION_HEX < 0x03000000 - if (RefInt_CheckExact(pyobject)) { - para.fValue.fVoidp = (void*)&((PyIntObject*)pyobject)->ob_ival; - para.fTypeCode = 'V'; - return true; - } -#endif -#if PY_VERSION_HEX >= 0x02050000 if (Py_TYPE(pyobject) == GetCTypesType(ct_c_int)) { para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr; para.fTypeCode = 'V'; return true; } -#endif // alternate, pass pointer from buffer Py_ssize_t buflen = Utility::GetBuffer(pyobject, 'i', sizeof(int), para.fValue.fVoidp); @@ -857,11 +816,7 @@ bool CPyCppyy::IntRefConverter::SetArg( return true; }; -#if PY_VERSION_HEX < 0x02050000 - PyErr_SetString(PyExc_TypeError, "use cppyy.Long for pass-by-ref of ints"); -#else PyErr_SetString(PyExc_TypeError, "use ctypes.c_int for pass-by-ref of ints"); -#endif return false; } @@ -1170,21 +1125,12 @@ bool CPyCppyy::DoubleRefConverter::SetArg( PyObject* pyobject, Parameter& para, CallContext* /* ctxt */) { // convert to C++ double&, set arg for call -#if PY_VERSION_HEX < 0x03000000 - if (RefFloat_CheckExact(pyobject)) { - para.fValue.fVoidp = (void*)&((PyFloatObject*)pyobject)->ob_fval; - para.fTypeCode = 'V'; - return true; - } -#endif -#if PY_VERSION_HEX >= 0x02050000 if (Py_TYPE(pyobject) == GetCTypesType(ct_c_double)) { para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr; para.fTypeCode = 'V'; return true; } -#endif // alternate, pass pointer from buffer Py_ssize_t buflen = Utility::GetBuffer(pyobject, 'd', sizeof(double), para.fValue.fVoidp); @@ -1193,11 +1139,7 @@ bool CPyCppyy::DoubleRefConverter::SetArg( return true; } -#if PY_VERSION_HEX < 0x02050000 - PyErr_SetString(PyExc_TypeError, "use cppyy.Double for pass-by-ref of doubles"); -#else PyErr_SetString(PyExc_TypeError, "use ctypes.c_double for pass-by-ref of doubles"); -#endif return false; } @@ -1665,7 +1607,7 @@ namespace CPyCppyy { class StdSpanConverter : public InstanceConverter { public: - StdSpanConverter(std::string const &typeName, Cppyy::TCppType_t klass, bool keepControl = false) + StdSpanConverter(std::string const &typeName, Cppyy::TCppScope_t klass, bool keepControl = false) : InstanceConverter{klass, keepControl}, fTypeName{typeName} { } @@ -1894,9 +1836,7 @@ bool CPyCppyy::CStringArrayConverter::SetArg( return true; } else if (PySequence_Check(pyobject) && !CPyCppyy_PyText_Check(pyobject) -#if PY_VERSION_HEX >= 0x03000000 && !PyBytes_Check(pyobject) -#endif ) { //for (auto& p : fBuffer) free(p); fBuffer.clear(); @@ -1975,12 +1915,7 @@ static inline bool CPyCppyy_PyUnicodeAsBytes2Buffer(PyObject* pyobject, T& buffe Py_INCREF(pyobject); pybytes = pyobject; } else if (PyUnicode_Check(pyobject)) { -#if PY_VERSION_HEX < 0x03030000 - pybytes = PyUnicode_EncodeUTF8( - PyUnicode_AS_UNICODE(pyobject), CPyCppyy_PyUnicode_GET_SIZE(pyobject), nullptr); -#else pybytes = PyUnicode_AsUTF8String(pyobject); -#endif } if (pybytes) { @@ -2023,7 +1958,7 @@ PyObject* CPyCppyy::name##Converter::FromMemory(void* address) \ if (address) \ return InstanceConverter::FromMemory(address); \ auto* empty = new type(); \ - return BindCppObjectNoCast(empty, fClass, CPPInstance::kIsOwner); \ + return BindCppObjectNoCast(Cppyy::TCppObject_t((void*)empty), fClass, CPPInstance::kIsOwner);\ } \ \ bool CPyCppyy::name##Converter::ToMemory( \ @@ -2051,23 +1986,6 @@ bool CPyCppyy::STLWStringConverter::SetArg( para.fTypeCode = 'V'; return true; } -#if PY_VERSION_HEX < 0x03000000 - else if (PyString_Check(pyobject)) { -#ifdef HAS_CODECVT - std::wstring_convert> cnv; - fBuffer = cnv.from_bytes(PyString_AS_STRING(pyobject)); -#else - PyObject* pyu = PyUnicode_FromString(PyString_AS_STRING(pyobject)); - if (!pyu) return false; - Py_ssize_t len = CPyCppyy_PyUnicode_GET_SIZE(pyu); - fBuffer.resize(len); - CPyCppyy_PyUnicode_AsWideChar(pyu, &fBuffer[0], len); -#endif - para.fValue.fVoidp = &fBuffer; - para.fTypeCode = 'V'; - return true; - } -#endif if (!(PyInt_Check(pyobject) || PyLong_Check(pyobject))) { bool result = InstancePtrConverter::SetArg(pyobject, para, ctxt); @@ -2214,7 +2132,7 @@ bool CPyCppyy::InstancePtrConverter::SetArg( PyObject* pyobject, Parameter& para, CallContext* ctxt) { // convert to C++ instance*, set arg for call - CPPInstance* pyobj = GetCppInstance(pyobject, ISCONST ? fClass : (Cppyy::TCppType_t)0); + CPPInstance* pyobj = GetCppInstance(pyobject, ISCONST ? fClass : Cppyy::TCppScope_t{}); if (!pyobj) { if (GetAddressSpecialCase(pyobject, para.fValue.fVoidp)) { para.fTypeCode = 'p'; // allow special cases such as nullptr @@ -2230,7 +2148,7 @@ bool CPyCppyy::InstancePtrConverter::SetArg( if (pyobj->IsSmart() && IsConstructor(ctxt->fFlags) && Cppyy::IsSmartPtr(ctxt->fCurScope)) return false; - Cppyy::TCppType_t oisa = pyobj->ObjectIsA(); + Cppyy::TCppScope_t oisa = pyobj->ObjectIsA(); if (oisa && (oisa == fClass || Cppyy::IsSubclass(oisa, fClass))) { // depending on memory policy, some objects need releasing when passed into functions if (!KeepControl() && !UseStrictOwnership(ctxt)) @@ -2266,7 +2184,7 @@ template bool CPyCppyy::InstancePtrConverter::ToMemory(PyObject* value, void* address, PyObject* /* ctxt */) { // convert to C++ instance, write it at
- CPPInstance* pyobj = GetCppInstance(value, ISCONST ? fClass : (Cppyy::TCppType_t)0); + CPPInstance* pyobj = GetCppInstance(value, ISCONST ? fClass : Cppyy::TCppScope_t{}); if (!pyobj) { void* ptr = nullptr; if (GetAddressSpecialCase(value, ptr)) { @@ -2337,11 +2255,7 @@ bool CPyCppyy::InstanceConverter::ToMemory(PyObject* value, void* address, PyObj { // assign value to C++ instance living at
through assignment operator PyObject* pyobj = BindCppObjectNoCast(address, fClass); -#if PY_VERSION_HEX >= 0x03080000 PyObject* result = PyObject_CallMethodOneArg(pyobj, PyStrings::gAssign, value); -#else - PyObject* result = PyObject_CallMethod(pyobj, (char*)"__assign__", (char*)"O", value); -#endif Py_DECREF(pyobj); if (result) { @@ -2357,7 +2271,7 @@ bool CPyCppyy::InstanceRefConverter::SetArg( PyObject* pyobject, Parameter& para, CallContext* ctxt) { // convert to C++ instance&, set arg for call - CPPInstance* pyobj = GetCppInstance(pyobject, fIsConst ? fClass : (Cppyy::TCppType_t)0); + CPPInstance* pyobj = GetCppInstance(pyobject, fIsConst ? fClass : Cppyy::TCppScope_t{}); if (pyobj) { // reject moves @@ -2367,7 +2281,7 @@ bool CPyCppyy::InstanceRefConverter::SetArg( // smart pointers can end up here in case of a move, so preferentially match // the smart type directly bool argset = false; - Cppyy::TCppType_t cls = 0; + Cppyy::TCppScope_t cls; if (pyobj->IsSmart()) { cls = pyobj->ObjectIsA(false); if (cls && Cppyy::IsSubclass(cls, fClass)) { @@ -2701,10 +2615,10 @@ bool CPyCppyy::PyObjectConverter::ToMemory(PyObject* value, void* address, PyObj static unsigned int sWrapperCounter = 0; // cache mapping signature/return type to python callable and corresponding wrapper typedef std::string RetSigKey_t; -static std::map> sWrapperFree; -static std::map> sWrapperLookup; -static std::map> sWrapperWeakRefs; -static std::map sWrapperReference; +static std::unordered_map> sWrapperFree; +static std::unordered_map> sWrapperLookup; +static std::unordered_map> sWrapperWeakRefs; +static std::unordered_map sWrapperReference; static PyObject* WrapperCacheEraser(PyObject*, PyObject* pyref) { @@ -3004,10 +2918,10 @@ bool CPyCppyy::SmartPtrConverter::SetArg( } CPPInstance* pyobj = (CPPInstance*)pyobject; - Cppyy::TCppType_t oisa = pyobj->ObjectIsA(); + Cppyy::TCppScope_t oisa = pyobj->ObjectIsA(); // for the case where we have a 'hidden' smart pointer: - if (Cppyy::TCppType_t tsmart = pyobj->GetSmartIsA()) { + if (Cppyy::TCppScope_t tsmart = pyobj->GetSmartIsA()) { if (Cppyy::IsSubclass(tsmart, fSmartPtrType)) { // depending on memory policy, some objects need releasing when passed into functions if (!fKeepControl && !UseStrictOwnership(ctxt)) @@ -3060,7 +2974,10 @@ bool CPyCppyy::SmartPtrConverter::SetArg( } // final option, try mapping pointer types held (TODO: do not allow for non-const ref) - if (pyobj->IsSmart() && Cppyy::IsSubclass(oisa, fUnderlyingType)) { +// Match on the declared underlying type, not the (possibly downcast) +// dereferenced object: a unique_ptr holding a Derived is still a +// unique_ptr and must not pass where a unique_ptr is expected. + if (pyobj->IsSmart() && Cppyy::IsSubclass(pyobj->GetSmartUnderlyingType(), fUnderlyingType)) { para.fValue.fVoidp = ((CPPInstance*)pyobject)->GetSmartObject(); para.fTypeCode = 'V'; return true; @@ -3082,11 +2999,7 @@ bool CPyCppyy::SmartPtrConverter::ToMemory(PyObject* value, void* address, PyObj // assign value to C++ instance living at
through assignment operator (this // is similar to InstanceConverter::ToMemory, but prevents wrapping the smart ptr) PyObject* pyobj = BindCppObjectNoCast(address, fSmartPtrType, CPPInstance::kNoWrapConv); -#if PY_VERSION_HEX >= 0x03080000 PyObject* result = PyObject_CallMethodOneArg(pyobj, PyStrings::gAssign, value); -#else - PyObject* result = PyObject_CallMethod(pyobj, (char*)"__assign__", (char*)"O", value); -#endif Py_DECREF(pyobj); if (result) { @@ -3122,7 +3035,7 @@ struct faux_initlist } // unnamed namespace -CPyCppyy::InitializerListConverter::InitializerListConverter(Cppyy::TCppType_t klass, std::string const &value_type) +CPyCppyy::InitializerListConverter::InitializerListConverter(Cppyy::TCppScope_t klass, std::string const &value_type) : InstanceConverter{klass}, fValueTypeName{value_type}, @@ -3167,12 +3080,7 @@ bool CPyCppyy::InitializerListConverter::SetArg( // convert the given argument to an initializer list temporary; this is purely meant // to be a syntactic thing, so only _python_ sequences are allowed; bound C++ proxies // (likely explicitly created std::initializer_list, go through an instance converter - if (!PySequence_Check(pyobject) || CPyCppyy_PyText_Check(pyobject) -#if PY_VERSION_HEX >= 0x03000000 - || PyBytes_Check(pyobject) -#else - || PyUnicode_Check(pyobject) -#endif + if (!PySequence_Check(pyobject) || CPyCppyy_PyText_Check(pyobject) || PyBytes_Check(pyobject) ) return false; @@ -3235,7 +3143,7 @@ bool CPyCppyy::InitializerListConverter::SetArg( // we need to construct a default object for the constructor to assign into; this is // clunky, but the use of a copy constructor isn't much better as the Python object // need not be a C++ object - memloc = (void*)Cppyy::Construct(fValueType, memloc); + memloc = (void*)Cppyy::Construct(fValueType, memloc).data; // We checked above that we are able to construct default objects of fValueType. assert(memloc && ("failed to default construct object for type " + fValueTypeName).c_str()); entries += 1; @@ -3448,35 +3356,27 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim } } -// FIXME: Taken from ROOT, update this to use CppInterOp for span check and extracting value type #if __cplusplus >= 202002L //-- special case: std::span pos = resolvedType.find("span<"); if (pos == 0 /* no std:: */ || pos == 5 /* with std:: */ || pos == 6 /* const no std:: */ || pos == 11 /* const with std:: */ ) { - auto pos1 = realType.find('<'); - auto pos21 = realType.find(','); // for the case there are more template args - auto pos22 = realType.find('>'); - auto len = std::min(pos21 - pos1, pos22 - pos1) - 1; - std::string value_type = realType.substr(pos1+1, len); - - // strip leading "const " - const std::string cprefix = "const "; - if (value_type.compare(0, cprefix.size(), cprefix) == 0) { - value_type = value_type.substr(cprefix.size()); + // std::span::value_type == std::remove_cv_t: look it up + Cppyy::TCppScope_t span_scope = Cppyy::GetScope(realType); + Cppyy::TCppType_t vtype = Cppyy::ResolveType( + Cppyy::GetTypeFromScope(Cppyy::GetNamed("value_type", span_scope))); + if (span_scope && vtype) { + std::string value_type = Cppyy::GetTypeAsString(vtype); + return new StdSpanConverter{value_type, span_scope}; } - - std::string span_type = "std::span<" + value_type + ">"; - - return new StdSpanConverter{value_type, Cppyy::GetScope(span_type)}; } #endif // converters for known C++ classes and default (void*) Converter* result = nullptr; if (Cppyy::TCppScope_t klass = Cppyy::GetFullScope(realType)) { - Cppyy::TCppType_t raw{0}; + Cppyy::TCppScope_t raw; if (Cppyy::GetSmartPtrInfo(realType, &raw, nullptr)) { if (cpd == "") { result = new SmartPtrConverter(klass, raw, control); @@ -3676,6 +3576,23 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(Cppyy::TCppType_t type, cdims_t d } } +#if __cplusplus >= 202002L +//-- special case: std::span + pos = resolvedTypeStr.find("span<"); + if (pos == 0 /* no std:: */ || pos == 5 /* with std:: */ || + pos == 6 /* const no std:: */ || pos == 11 /* const with std:: */ ) { + + // std::span::value_type == std::remove_cv_t: look it up + Cppyy::TCppScope_t span_scope = Cppyy::GetScopeFromType(realType); + Cppyy::TCppType_t vtype = Cppyy::ResolveType( + Cppyy::GetTypeFromScope(Cppyy::GetNamed("value_type", span_scope))); + if (span_scope && vtype) { + std::string value_type = Cppyy::GetTypeAsString(vtype); + return new StdSpanConverter{value_type, span_scope}; + } + } +#endif + // converters for known C++ classes and default (void*) Converter* result = nullptr; Cppyy::TCppScope_t klass = Cppyy::GetScopeFromType(realType); @@ -3691,7 +3608,7 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(Cppyy::TCppType_t type, cdims_t d 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}; + Cppyy::TCppScope_t raw; if (Cppyy::GetSmartPtrInfo(realTypeStr, &raw, nullptr)) { if (cpd == "") { result = new SmartPtrConverter(klass, raw, control); diff --git a/src/CPyCppyy/src/Converters.h b/src/CPyCppyy/src/Converters.h index 92cccba..6560e79 100644 --- a/src/CPyCppyy/src/Converters.h +++ b/src/CPyCppyy/src/Converters.h @@ -53,7 +53,7 @@ class VoidArrayConverter : public Converter { 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; } + std::string GetFailureMsg() override { return "[VoidArrayConverter] " + fFailureMsg; } protected: virtual bool GetAddressSpecialCase(PyObject* pyobject, void*& address); @@ -76,7 +76,7 @@ class InstancePtrConverter : public VoidArrayConverter { bool ToMemory(PyObject* value, void* address, PyObject* ctxt = nullptr) override; protected: - Cppyy::TCppType_t fClass; + Cppyy::TCppScope_t fClass; }; class StrictInstancePtrConverter : public InstancePtrConverter { diff --git a/src/CPyCppyy/src/Cppyy.h b/src/CPyCppyy/src/Cppyy.h index da6c3ab..440270c 100644 --- a/src/CPyCppyy/src/Cppyy.h +++ b/src/CPyCppyy/src/Cppyy.h @@ -43,25 +43,83 @@ typedef long double PY_LONG_DOUBLE; // provided through some factory methods in CppInterOp API, so the clients can // rely completely on opaque pointers like we do for the rest of the argument // types. -namespace CppImpl { struct TemplateArgInfo { void* m_Type; const char* m_IntegralValue; TemplateArgInfo(void* type, const char* integral_value = nullptr) : m_Type(type), m_IntegralValue(integral_value) {} }; -} // namespace CppImpl -namespace Cpp = CppImpl; +namespace Cpp { +using TemplateArgInfo = ::TemplateArgInfo; + +struct DeclRef { + void* data; + DeclRef() : data(nullptr) {} + DeclRef(void* P) : data(P) {} + DeclRef(decltype(nullptr)) : data(nullptr) {} + explicit operator bool() const { return data != nullptr; } + friend bool operator==(DeclRef a, DeclRef b) { return a.data == b.data; } + friend bool operator!=(DeclRef a, DeclRef b) { return !(a == b); } +}; -namespace Cppyy { +struct TypeRef { + void* data; + TypeRef() : data(nullptr) {} + TypeRef(void* P) : data(P) {} + TypeRef(decltype(nullptr)) : data(nullptr) {} + explicit operator bool() const { return data != nullptr; } + friend bool operator==(TypeRef a, TypeRef b) { return a.data == b.data; } + friend bool operator!=(TypeRef a, TypeRef b) { return !(a == b); } +}; + +struct FuncRef { + void* data; + FuncRef() : data(nullptr) {} + FuncRef(void* P) : data(P) {} + FuncRef(decltype(nullptr)) : data(nullptr) {} + explicit operator bool() const { return data != nullptr; } + friend bool operator==(FuncRef a, FuncRef b) { return a.data == b.data; } + friend bool operator!=(FuncRef a, FuncRef b) { return !(a == b); } +}; - typedef void* TCppScope_t; - typedef TCppScope_t TCppType_t; - typedef void* TCppEnum_t; - typedef void* TCppObject_t; - typedef void* TCppMethod_t; +struct ObjectRef { + void* data; + ObjectRef() : data(nullptr) {} + ObjectRef(void* P) : data(P) {} + ObjectRef(decltype(nullptr)) : data(nullptr) {} + explicit operator bool() const { return data != nullptr; } + friend bool operator==(ObjectRef a, ObjectRef b) { return a.data == b.data; } + friend bool operator!=(ObjectRef a, ObjectRef b) { return !(a == b); } +}; +} // namespace Cpp + +template <> struct std::hash { + std::size_t operator()(const Cpp::DeclRef &obj) const { + return std::hash{}(obj.data); + } +}; +template <> struct std::hash { + std::size_t operator()(const Cpp::TypeRef &obj) const { + return std::hash{}(obj.data); + } +}; +template <> struct std::hash { + std::size_t operator()(const Cpp::FuncRef &obj) const { + return std::hash{}(obj.data); + } +}; +template <> struct std::hash { + std::size_t operator()(const Cpp::ObjectRef &obj) const { + return std::hash{}(obj.data); + } +}; +namespace Cppyy { + typedef Cpp::DeclRef TCppScope_t; + typedef Cpp::TypeRef TCppType_t; + typedef Cpp::ObjectRef TCppObject_t; + typedef Cpp::FuncRef TCppMethod_t; typedef size_t TCppIndex_t; typedef void* TCppFuncAddr_t; @@ -69,29 +127,47 @@ namespace Cppyy { CPPYY_IMPORT bool Compile(const std::string& code, bool silent = false); CPPYY_IMPORT - std::string ToString(TCppType_t klass, TCppObject_t obj); + std::string ToString(TCppScope_t klass, TCppObject_t obj); // name to opaque C++ scope representation ----------------------------------- CPPYY_IMPORT std::string ResolveName(const std::string& cppitem_name); - + CPPYY_IMPORT + TCppType_t ResolveType(TCppType_t 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); + TCppType_t GetReferencedType(TCppType_t type, bool rvalue = false); + CPPYY_IMPORT + std::string ResolveEnum(TCppScope_t enum_scope); CPPYY_IMPORT - TCppScope_t GetScope(const std::string& name, TCppScope_t parent_scope = 0); + bool IsLValueReferenceType(TCppType_t type); + CPPYY_IMPORT + bool IsRValueReferenceType(TCppType_t type); + CPPYY_IMPORT + bool IsClassType(TCppType_t type); + CPPYY_IMPORT + bool IsIntegerType(TCppType_t type, bool* is_signed = nullptr); + CPPYY_IMPORT + bool IsPointerType(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, Cppyy::TCppScope_t parent = nullptr); + CPPYY_IMPORT + TCppType_t GetComplexType(const std::string &element_type); + CPPYY_IMPORT + TCppScope_t GetScope(const std::string& scope_name, + TCppScope_t parent_scope = TCppScope_t{}); CPPYY_IMPORT TCppScope_t GetUnderlyingScope(TCppScope_t scope); CPPYY_IMPORT @@ -100,11 +176,11 @@ namespace Cppyy { TCppScope_t GetTypeScope(TCppScope_t klass); CPPYY_IMPORT TCppScope_t GetNamed(const std::string& scope_name, - TCppScope_t parent_scope = 0); + TCppScope_t parent_scope = TCppScope_t{}); CPPYY_IMPORT TCppScope_t GetParentScope(TCppScope_t scope); CPPYY_IMPORT - TCppScope_t GetScopeFromType(TCppScope_t type); + TCppScope_t GetScopeFromType(TCppType_t type); CPPYY_IMPORT TCppType_t GetTypeFromScope(TCppScope_t klass); CPPYY_IMPORT @@ -115,29 +191,25 @@ namespace Cppyy { 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(TCppScope_t scope); CPPYY_IMPORT - bool IsPointerType(TCppType_t type); + bool IsBuiltin(TCppType_t type); CPPYY_IMPORT - TCppScope_t gGlobalScope; // for fast access + bool IsComplete(TCppScope_t type); // memory management --------------------------------------------------------- CPPYY_IMPORT - TCppObject_t Allocate(TCppType_t type); + TCppObject_t Allocate(TCppScope_t scope); CPPYY_IMPORT - void Deallocate(TCppType_t type, TCppObject_t instance); + void Deallocate(TCppScope_t scope, TCppObject_t instance); CPPYY_IMPORT - TCppObject_t Construct(TCppType_t type, void* arena = nullptr); + TCppObject_t Construct(TCppScope_t scope, void* arena = nullptr); CPPYY_IMPORT - void Destruct(TCppType_t type, TCppObject_t instance); + void Destruct(TCppScope_t scope, TCppObject_t instance); // method/function dispatching ----------------------------------------------- CPPYY_IMPORT @@ -160,6 +232,7 @@ namespace Cppyy { double CallD(TCppMethod_t method, TCppObject_t self, size_t nargs, void* args); CPPYY_IMPORT PY_LONG_DOUBLE CallLD(TCppMethod_t method, TCppObject_t self, size_t nargs, void* args); + CPPYY_IMPORT void* CallR(TCppMethod_t method, TCppObject_t self, size_t nargs, void* args); CPPYY_IMPORT @@ -167,7 +240,7 @@ namespace Cppyy { CPPYY_IMPORT TCppObject_t CallConstructor(TCppMethod_t method, TCppScope_t klass, size_t nargs, void* args); CPPYY_IMPORT - void CallDestructor(TCppType_t type, TCppObject_t self); + void CallDestructor(TCppScope_t type, TCppObject_t self); CPPYY_IMPORT TCppObject_t CallO(TCppMethod_t method, TCppObject_t self, size_t nargs, void* args, TCppType_t result_type); @@ -190,13 +263,13 @@ namespace Cppyy { CPPYY_IMPORT bool IsClass(TCppScope_t scope); CPPYY_IMPORT - bool IsTemplate(TCppScope_t handle); + bool IsTemplate(TCppScope_t scope); CPPYY_IMPORT - bool IsTemplateInstantiation(TCppScope_t handle); + bool IsTemplateInstantiation(TCppScope_t scope); CPPYY_IMPORT - bool IsTypedefed(TCppScope_t handle); + bool IsTypedefed(TCppScope_t scope); CPPYY_IMPORT - bool IsAbstract(TCppType_t type); + bool IsAbstract(TCppScope_t scope); CPPYY_IMPORT bool IsEnumScope(TCppScope_t scope); CPPYY_IMPORT @@ -204,7 +277,7 @@ namespace Cppyy { CPPYY_IMPORT bool IsEnumType(TCppType_t type); CPPYY_IMPORT - bool IsAggregate(TCppType_t type); + bool IsAggregate(TCppScope_t type); CPPYY_IMPORT bool IsDefaultConstructable(TCppScope_t scope); CPPYY_IMPORT @@ -215,57 +288,44 @@ namespace Cppyy { // namespace reflection information ------------------------------------------ CPPYY_IMPORT - std::vector GetUsingNamespaces(TCppScope_t); + std::vector GetUsingNamespaces(TCppScope_t); // class reflection information ---------------------------------------------- CPPYY_IMPORT - std::string GetFinalName(TCppType_t type); - CPPYY_IMPORT - std::string GetScopedFinalName(TCppType_t type); - CPPYY_IMPORT - bool HasVirtualDestructor(TCppType_t type); + std::string GetFinalName(TCppScope_t type); CPPYY_IMPORT - bool HasComplexHierarchy(TCppType_t type); + std::string GetScopedFinalName(TCppScope_t type); CPPYY_IMPORT - TCppIndex_t GetNumBases(TCppType_t type); + bool HasVirtualDestructor(TCppScope_t type); CPPYY_IMPORT - TCppIndex_t GetNumBasesLongestBranch(TCppType_t type); + TCppIndex_t GetNumBases(TCppScope_t klass); CPPYY_IMPORT - std::string GetBaseName(TCppType_t type, TCppIndex_t ibase); + TCppIndex_t GetNumBasesLongestBranch(TCppScope_t klass); CPPYY_IMPORT - TCppScope_t GetBaseScope(TCppType_t type, TCppIndex_t ibase); + std::string GetBaseName(TCppScope_t klass, TCppIndex_t ibase); CPPYY_IMPORT - bool IsSubclass(TCppType_t derived, TCppType_t base); + TCppScope_t GetBaseScope(TCppScope_t klass, TCppIndex_t ibase); CPPYY_IMPORT - bool IsSmartPtr(TCppType_t type); + bool IsSubclass(TCppScope_t derived, TCppScope_t base); CPPYY_IMPORT - bool GetSmartPtrInfo(const std::string&, TCppType_t* raw, TCppMethod_t* deref); - CPPYY_IMPORT - void AddSmartPtrType(const std::string&); - + bool IsSmartPtr(TCppScope_t klass); CPPYY_IMPORT - void AddTypeReducer(const std::string& reducable, const std::string& reduced); - + bool GetSmartPtrInfo(const std::string&, TCppScope_t* raw, TCppMethod_t* deref); // calculate offsets between declared and actual type, up-cast: direction > 0; down-cast: direction < 0 CPPYY_IMPORT ptrdiff_t GetBaseOffset( - TCppType_t derived, TCppType_t base, TCppObject_t address, int direction, bool rerror = false); + TCppScope_t derived, TCppScope_t base, TCppObject_t address, int direction, bool rerror = false); // method/function reflection information ------------------------------------ CPPYY_IMPORT void GetClassMethods(TCppScope_t scope, std::vector &methods); CPPYY_IMPORT - std::vector GetMethodsFromName(TCppScope_t scope, const std::string& name); - - CPPYY_IMPORT - TCppMethod_t GetMethod(TCppScope_t scope, TCppIndex_t imeth); - - CPPYY_IMPORT - std::string GetMethodName(TCppMethod_t); + std::vector GetMethodsFromName(TCppScope_t scope, + const std::string& name); CPPYY_IMPORT - std::string GetMethodFullName(TCppMethod_t); + std::string GetName(TCppScope_t); CPPYY_IMPORT - std::string GetMethodMangledName(TCppMethod_t); + std::string GetFullName(TCppScope_t); CPPYY_IMPORT TCppType_t GetMethodReturnType(TCppMethod_t); CPPYY_IMPORT @@ -279,19 +339,23 @@ namespace Cppyy { CPPYY_IMPORT TCppType_t GetMethodArgType(TCppMethod_t, TCppIndex_t iarg); CPPYY_IMPORT - std::string GetMethodArgTypeAsString(TCppMethod_t, TCppIndex_t iarg); + TCppIndex_t CompareMethodArgType(TCppMethod_t, TCppIndex_t iarg, const std::string &req_type); CPPYY_IMPORT - std::string GetMethodArgCanonTypeAsString(TCppMethod_t, TCppIndex_t iarg); + std::string GetMethodArgTypeAsString(TCppMethod_t method, TCppIndex_t iarg); CPPYY_IMPORT - TCppIndex_t CompareMethodArgType(TCppMethod_t, TCppIndex_t iarg, const std::string &req_type); + std::string GetMethodArgCanonTypeAsString(TCppMethod_t method, TCppIndex_t iarg); CPPYY_IMPORT std::string GetMethodArgDefault(TCppMethod_t, TCppIndex_t iarg); CPPYY_IMPORT std::string GetMethodSignature(TCppMethod_t, bool show_formal_args, TCppIndex_t max_args = (TCppIndex_t)-1); + // GetMethodPrototype is unused. CPPYY_IMPORT std::string GetMethodPrototype(TCppMethod_t, bool show_formal_args); CPPYY_IMPORT + std::string GetDoxygenComment(TCppScope_t scope, bool strip_markers = true); + CPPYY_IMPORT bool IsConstMethod(TCppMethod_t); +// Templated method/function reflection information ------------------------------------ CPPYY_IMPORT void GetTemplatedMethods(TCppScope_t scope, std::vector &methods); CPPYY_IMPORT @@ -307,11 +371,12 @@ namespace Cppyy { CPPYY_IMPORT TCppMethod_t GetMethodTemplate( TCppScope_t scope, const std::string& name, const std::string& proto); - CPPYY_IMPORT - TCppMethod_t GetGlobalOperator(TCppType_t scope, const std::string &lc, - const std::string &rc, - const std::string &op); + void GetClassOperators(Cppyy::TCppScope_t klass, const std::string& opname, + std::vector& operators); + CPPYY_IMPORT + TCppMethod_t GetGlobalOperator( + TCppScope_t scope, const std::string& lc, const std::string& rc, const std::string& op); // method properties --------------------------------------------------------- CPPYY_IMPORT @@ -321,6 +386,8 @@ namespace Cppyy { CPPYY_IMPORT bool IsProtectedMethod(TCppMethod_t method); CPPYY_IMPORT + bool IsPrivateMethod(TCppMethod_t method); + CPPYY_IMPORT bool IsConstructor(TCppMethod_t method); CPPYY_IMPORT bool IsDestructor(TCppMethod_t method); @@ -333,72 +400,52 @@ namespace Cppyy { CPPYY_IMPORT void GetDatamembers(TCppScope_t scope, std::vector& datamembers); CPPYY_IMPORT - std::string GetDatamemberName(TCppScope_t scope, TCppIndex_t idata); - CPPYY_IMPORT - TCppScope_t ReduceReturnType(TCppScope_t fn, TCppType_t reduce); bool IsLambdaClass(TCppType_t type); CPPYY_IMPORT TCppScope_t WrapLambdaFromVariable(TCppScope_t var); CPPYY_IMPORT - TCppScope_t AdaptFunctionForLambdaReturn(TCppScope_t fn); - CPPYY_IMPORT - TCppType_t GetDatamemberType(TCppScope_t var); + TCppMethod_t AdaptFunctionForLambdaReturn(TCppMethod_t fn); CPPYY_IMPORT - std::string GetTypeAsString(TCppType_t type); - CPPYY_IMPORT - bool IsRValueReferenceType(TCppType_t type); - CPPYY_IMPORT - bool IsLValueReferenceType(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); + TCppType_t GetDatamemberType(TCppScope_t data); CPPYY_IMPORT std::string GetDatamemberTypeAsString(TCppScope_t var); CPPYY_IMPORT + std::string GetTypeAsString(TCppType_t type); + CPPYY_IMPORT intptr_t GetDatamemberOffset(TCppScope_t var, TCppScope_t klass = nullptr); CPPYY_IMPORT - bool CheckDatamember(TCppScope_t scope, const std::string& name); + bool CheckDatamember(TCppScope_t scope, const std::string& name); -// data member properties ---------------------------------------------------- +// // data member properties ---------------------------------------------------- CPPYY_IMPORT - bool IsPublicData(TCppScope_t data); + bool IsPublicData(TCppScope_t var); CPPYY_IMPORT bool IsProtectedData(TCppScope_t var); CPPYY_IMPORT + bool IsPrivateData(TCppScope_t var); + CPPYY_IMPORT bool IsStaticDatamember(TCppScope_t var); CPPYY_IMPORT bool IsConstVar(TCppScope_t var); CPPYY_IMPORT - bool IsEnumData(TCppScope_t scope, TCppIndex_t idata); + TCppMethod_t ReduceReturnType(TCppMethod_t fn, TCppType_t reduce); CPPYY_IMPORT 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 std::vector GetEnumConstants(TCppScope_t scope); CPPYY_IMPORT TCppType_t GetEnumConstantType(TCppScope_t scope); CPPYY_IMPORT TCppIndex_t GetEnumDataValue(TCppScope_t scope); + CPPYY_IMPORT TCppScope_t InstantiateTemplate( TCppScope_t tmpl, Cpp::TemplateArgInfo* args, size_t args_size); CPPYY_IMPORT - TCppScope_t DumpScope(TCppScope_t scope); + void DumpScope(TCppScope_t scope); } // namespace Cppyy #endif // !CPYCPPYY_CPPYY_H diff --git a/src/CPyCppyy/src/CustomPyTypes.cxx b/src/CPyCppyy/src/CustomPyTypes.cxx index 6c7b835..4038947 100644 --- a/src/CPyCppyy/src/CustomPyTypes.cxx +++ b/src/CPyCppyy/src/CustomPyTypes.cxx @@ -13,72 +13,18 @@ // same, because the actual C++ type of the PyObject is PyMethodObject anyway. #define CustomInstanceMethod_GET_SELF(meth) reinterpret_cast(meth)->im_self #define CustomInstanceMethod_GET_FUNCTION(meth) reinterpret_cast(meth)->im_func -#if PY_VERSION_HEX >= 0x03000000 // TODO: this will break functionality #define CustomInstanceMethod_GET_CLASS(meth) Py_None -#else -#define CustomInstanceMethod_GET_CLASS(meth) PyMethod_GET_CLASS(meth) -#endif namespace CPyCppyy { -#if PY_VERSION_HEX < 0x03000000 -//= float type allowed for reference passing ================================= -PyTypeObject RefFloat_Type = { // python float is a C/C++ double - PyVarObject_HEAD_INIT(&PyType_Type, 0) - (char*)"cppyy.Double", // tp_name - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | - Py_TPFLAGS_BASETYPE, // tp_flags - (char*)"CPyCppyy float object for pass by reference", // tp_doc - 0, 0, 0, 0, 0, 0, 0, 0, 0, - &PyFloat_Type, // tp_base - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -}; - -//= long type allowed for reference passing ================================== -PyTypeObject RefInt_Type = { // python int is a C/C++ long - PyVarObject_HEAD_INIT(&PyType_Type, 0) - (char*)"cppyy.Long", // tp_name - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | - Py_TPFLAGS_BASETYPE -#if PY_VERSION_HEX >= 0x03040000 - | Py_TPFLAGS_LONG_SUBCLASS -#endif - , // tp_flags - (char*)"CPyCppyy long object for pass by reference", // tp_doc - 0, 0, 0, 0, 0, 0, 0, 0, 0, - &PyInt_Type, // tp_base - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -}; -#endif - //- custom type representing typedef to pointer of class --------------------- static PyObject* tptc_call(typedefpointertoclassobject* self, PyObject* args, PyObject* /* kwds */) { long long addr = 0; if (!PyArg_ParseTuple(args, const_cast("|L"), &addr)) return nullptr; - return BindCppObjectNoCast((Cppyy::TCppObject_t)(intptr_t)addr, self->fCppType); + return BindCppObjectNoCast(Cppyy::TCppObject_t((void*)addr), self->fCppType); } //----------------------------------------------------------------------------- @@ -154,25 +100,12 @@ PyTypeObject TypedefPointerToClass_Type = { 0, // tp_mro 0, // tp_cache 0, // tp_subclasses - 0 // tp_weaklist -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, // tp_weaklist + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; //= instancemethod object with a more efficient call function ================ @@ -183,11 +116,7 @@ static int numfree = 0; #endif //----------------------------------------------------------------------------- -PyObject* CustomInstanceMethod_New(PyObject* func, PyObject* self, PyObject* -#if PY_VERSION_HEX < 0x03000000 - pyclass -#endif - ) +PyObject* CustomInstanceMethod_New(PyObject* func, PyObject* self, PyObject*) { // from instancemethod, but with custom type (at issue is that instancemethod is not // meant to be derived from) @@ -214,10 +143,6 @@ PyObject* CustomInstanceMethod_New(PyObject* func, PyObject* self, PyObject* im->im_func = func; Py_XINCREF(self); im->im_self = self; -#if PY_VERSION_HEX < 0x03000000 - Py_XINCREF(pyclass); - im->im_class = pyclass; -#endif PyObject_GC_Track(im); return (PyObject*)im; } @@ -234,9 +159,6 @@ static void im_dealloc(PyMethodObject* im) Py_DECREF(im->im_func); Py_XDECREF(im->im_self); -#if PY_VERSION_HEX < 0x03000000 - Py_XDECREF(im->im_class); -#endif if (numfree < PyMethod_MAXFREELIST) { im->im_self = (PyObject*)free_list; @@ -296,12 +218,7 @@ static PyObject* im_descr_get(PyObject* meth, PyObject* obj, PyObject* pyclass) { // from instancemethod: don't rebind an already bound method, or an unbound method // of a class that's not a base class of pyclass - if (CustomInstanceMethod_GET_SELF(meth) -#if PY_VERSION_HEX < 0x03000000 - || (CustomInstanceMethod_GET_CLASS(meth) && - !PyObject_IsSubclass(pyclass, CustomInstanceMethod_GET_CLASS(meth))) -#endif - ) { + if (CustomInstanceMethod_GET_SELF(meth)) { Py_INCREF(meth); return meth; } @@ -328,25 +245,12 @@ PyTypeObject CustomInstanceMethod_Type = { &PyMethod_Type, // tp_base 0, im_descr_get, // tp_descr_get - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; @@ -388,25 +292,12 @@ PyTypeObject IndexIter_Type = { 0, 0, 0, PyObject_SelfIter, // tp_iter (iternextfunc)indexiter_iternext, // tp_iternext - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; @@ -428,9 +319,9 @@ static PyObject* vectoriter_iternext(vectoriterobject* vi) { // The CPPInstance::kNoMemReg by-passes the memory regulator; the assumption here is // that objects in vectors are simple and thus do not need to maintain object identity // (or at least not during the loop anyway). This gains 2x in performance. - Cppyy::TCppObject_t cppobj = (Cppyy::TCppObject_t)((ptrdiff_t)vi->vi_data + vi->vi_stride * vi->ii_pos); + Cppyy::TCppObject_t cppobj((void*)((ptrdiff_t)vi->vi_data + vi->vi_stride * vi->ii_pos)); if (vi->vi_flags & vectoriterobject::kIsPolymorphic) - result = CPyCppyy::BindCppObject(*(void**)cppobj, vi->vi_klass, CPyCppyy::CPPInstance::kNoMemReg); + result = CPyCppyy::BindCppObject(*(void**)cppobj.data, vi->vi_klass, CPyCppyy::CPPInstance::kNoMemReg); else result = CPyCppyy::BindCppObjectNoCast(cppobj, vi->vi_klass, CPyCppyy::CPPInstance::kNoMemReg); if ((vi->vi_flags & vectoriterobject::kNeedLifeLine) && result) @@ -459,25 +350,12 @@ PyTypeObject VectorIter_Type = { 0, 0, 0, PyObject_SelfIter, // tp_iter (iternextfunc)vectoriter_iternext, // tp_iternext - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; } // namespace CPyCppyy diff --git a/src/CPyCppyy/src/CustomPyTypes.h b/src/CPyCppyy/src/CustomPyTypes.h index b9fbf07..6e72ac8 100644 --- a/src/CPyCppyy/src/CustomPyTypes.h +++ b/src/CPyCppyy/src/CustomPyTypes.h @@ -1,48 +1,19 @@ #ifndef CPYCPPYY_CUSTOMPYTYPES_H #define CPYCPPYY_CUSTOMPYTYPES_H +#include "Python.h" +#include "Cppyy.h" + namespace CPyCppyy { /** Custom "builtins," detectable by type, for pass by ref and improved performance. */ -#if PY_VERSION_HEX < 0x03000000 -//- reference float object type and type verification ------------------------ -extern PyTypeObject RefFloat_Type; - -template -inline bool RefFloat_Check(T* object) -{ - return object && PyObject_TypeCheck(object, &RefFloat_Type); -} - -template -inline bool RefFloat_CheckExact(T* object) -{ - return object && Py_TYPE(object) == &RefFloat_Type; -} - -//- reference long object type and type verification ------------------------- -extern PyTypeObject RefInt_Type; - -template -inline bool RefInt_Check(T* object) -{ - return object && PyObject_TypeCheck(object, &RefInt_Type); -} - -template -inline bool RefInt_CheckExact(T* object) -{ - return object && Py_TYPE(object) == &RefInt_Type; -} -#endif - //- custom type representing typedef to pointer of class --------------------- struct typedefpointertoclassobject { PyObject_HEAD - Cppyy::TCppType_t fCppType; + Cppyy::TCppScope_t fCppType; }; extern PyTypeObject TypedefPointerToClass_Type; @@ -91,7 +62,7 @@ struct vectoriterobject : public indexiterobject { void* vi_data; Py_ssize_t vi_stride; CPyCppyy::Converter* vi_converter; - Cppyy::TCppType_t vi_klass; + Cppyy::TCppScope_t vi_klass; int vi_flags; enum EFlags { diff --git a/src/CPyCppyy/src/DeclareConverters.h b/src/CPyCppyy/src/DeclareConverters.h index 302027f..478f816 100644 --- a/src/CPyCppyy/src/DeclareConverters.h +++ b/src/CPyCppyy/src/DeclareConverters.h @@ -3,6 +3,7 @@ // Bindings #include "Converters.h" +#include "Cppyy.h" #include "Dimensions.h" // Standard @@ -20,14 +21,14 @@ public: \ 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]"; } \ + std::string GetFailureMsg() override { return "[" #name "Converter]"; } \ }; \ \ class Const##name##RefConverter : public Converter { \ public: \ bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ PyObject* FromMemory(void*) override; \ - virtual std::string GetFailureMsg() { return "[Const" #name "RefConverter]"; }\ + std::string GetFailureMsg() override { return "[Const" #name "RefConverter]"; }\ } @@ -36,14 +37,14 @@ class name##Converter : public base##Converter { \ public: \ PyObject* FromMemory(void*) override; \ bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; \ - virtual std::string GetFailureMsg() { return "[" #name "Converter]"; } \ + std::string GetFailureMsg() override { return "[" #name "Converter]"; } \ }; \ \ class Const##name##RefConverter : public Converter { \ public: \ bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ PyObject* FromMemory(void*) override; \ - virtual std::string GetFailureMsg() { return "[Const" #name "RefConverter]"; }\ + std::string GetFailureMsg() override { return "[Const" #name "RefConverter]"; }\ } #define CPPYY_DECLARE_REFCONVERTER(name) \ @@ -51,7 +52,7 @@ class name##RefConverter : public Converter { \ public: \ bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; \ PyObject* FromMemory(void*) override; \ - virtual std::string GetFailureMsg() { return "[" #name "RefConverter]"; }\ + std::string GetFailureMsg() override { return "[" #name "RefConverter]"; }\ }; #define CPPYY_DECLARE_ARRAY_CONVERTER(name) \ @@ -63,8 +64,8 @@ public: \ 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]"; }\ + bool HasState() override { return true; } \ + std::string GetFailureMsg() override { return "[" #name "ArrayConverter]"; }\ protected: \ dims_t fShape; \ bool fIsFixed; \ @@ -137,7 +138,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]"; } + std::string GetFailureMsg() override { return "[CStringConverter]"; } protected: std::string fBuffer; @@ -151,7 +152,7 @@ class NonConstCStringConverter : public CStringConverter { public: bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; PyObject* FromMemory(void* address) override; - virtual std::string GetFailureMsg() { return "[NonConstCStringConverter]"; } + std::string GetFailureMsg() override { return "[NonConstCStringConverter]"; } }; class WCStringConverter : public Converter { @@ -167,7 +168,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]"; }; + std::string GetFailureMsg() override { return "[WCStringConverter]"; }; protected: wchar_t* fBuffer; @@ -187,7 +188,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]"; }; + std::string GetFailureMsg() override { return "[CString16Converter]"; }; protected: char16_t* fBuffer; @@ -207,7 +208,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]"; }; + std::string GetFailureMsg() override { return "[CString32Converter]"; }; protected: char32_t* fBuffer; @@ -243,7 +244,7 @@ class CStringArrayConverter : public SCharArrayConverter { using SCharArrayConverter::SCharArrayConverter; bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; PyObject* FromMemory(void* address) override; - virtual std::string GetFailureMsg() { return "[CStringArrayConverter]"; }; + std::string GetFailureMsg() override { return "[CStringArrayConverter]"; }; private: std::vector fBuffer; @@ -253,7 +254,7 @@ class NonConstCStringArrayConverter : public CStringArrayConverter { public: using CStringArrayConverter::CStringArrayConverter; PyObject* FromMemory(void* address) override; - virtual std::string GetFailureMsg() { return "[NonConstCStringArrayConverter]"; }; + std::string GetFailureMsg() override { return "[NonConstCStringArrayConverter]"; }; }; // converters for special cases @@ -268,30 +269,30 @@ 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]"; }; + std::string GetFailureMsg() override { return "[InstanceConverter]"; }; }; class InstanceRefConverter : public Converter { public: - InstanceRefConverter(Cppyy::TCppType_t klass, bool isConst) : + InstanceRefConverter(Cppyy::TCppScope_t klass, bool isConst) : fClass(klass), fIsConst(isConst) {} public: bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; PyObject* FromMemory(void* address) override; bool HasState() override { return true; } - virtual std::string GetFailureMsg() { return "[InstanceRefConverter]"; }; + std::string GetFailureMsg() override { return "[InstanceRefConverter]"; }; protected: - Cppyy::TCppType_t fClass; + Cppyy::TCppScope_t fClass; bool fIsConst; }; class InstanceMoveConverter : public InstanceRefConverter { public: - InstanceMoveConverter(Cppyy::TCppType_t klass) : InstanceRefConverter(klass, true) {} + InstanceMoveConverter(Cppyy::TCppScope_t klass) : InstanceRefConverter(klass, true) {} bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; - virtual std::string GetFailureMsg() { return "[InstanceMoveConverter]"; }; + std::string GetFailureMsg() override { return "[InstanceMoveConverter]"; }; }; template @@ -303,12 +304,12 @@ 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]"; }; + std::string GetFailureMsg() override { return "[InstancePtrPtrConverter]"; }; }; class InstanceArrayConverter : public InstancePtrConverter { public: - InstanceArrayConverter(Cppyy::TCppType_t klass, cdims_t dims, bool keepControl = false) : + InstanceArrayConverter(Cppyy::TCppScope_t klass, cdims_t dims, bool keepControl = false) : InstancePtrConverter(klass, keepControl), fShape(dims) { } InstanceArrayConverter(const InstanceArrayConverter&) = delete; InstanceArrayConverter& operator=(const InstanceArrayConverter&) = delete; @@ -317,7 +318,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]"; }; + std::string GetFailureMsg() override { return "[InstanceArrayConverter]"; }; protected: dims_t fShape; @@ -333,7 +334,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]"; }; + std::string GetFailureMsg() override { return "[ComplexDConverter]"; }; private: std::complex fBuffer; @@ -345,7 +346,7 @@ class ComplexDConverter: public InstanceConverter { class STLIteratorConverter : public Converter { public: bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; - virtual std::string GetFailureMsg() { return "[STLIteratorConverter]"; }; + std::string GetFailureMsg() override { return "[STLIteratorConverter]"; }; }; // -- END Cling WORKAROUND @@ -353,7 +354,7 @@ class STLIteratorConverter : public Converter { class VoidPtrRefConverter : public Converter { public: bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; - virtual std::string GetFailureMsg() { return "[VoidPtrRefConverter]"; }; + std::string GetFailureMsg() override { return "[VoidPtrRefConverter]"; }; }; class VoidPtrPtrConverter : public Converter { @@ -362,7 +363,7 @@ class VoidPtrPtrConverter : public Converter { bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; PyObject* FromMemory(void* address) override; bool HasState() override { return true; } - virtual std::string GetFailureMsg() { return "[VoidPtrPtrConverter] " + fFailureMsg; } + std::string GetFailureMsg() override { return "[VoidPtrPtrConverter] " + fFailureMsg; } protected: dims_t fShape; @@ -383,7 +384,7 @@ public: \ PyObject* FromMemory(void* address) override; \ bool ToMemory(PyObject*, void*, PyObject* = nullptr) override; \ bool HasState() override { return true; } \ - virtual std::string GetFailureMsg() { return "[" #name "Converter]"; }; \ + std::string GetFailureMsg() override { return "[" #name "Converter]"; }; \ protected: \ strtype fBuffer; \ } @@ -398,7 +399,7 @@ class STLStringMoveConverter : public STLStringConverter { public: bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; - virtual std::string GetFailureMsg() { return "[STLStringMoveConverter]"; }; + std::string GetFailureMsg() override { return "[STLStringMoveConverter]"; }; }; @@ -413,7 +414,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]"; }; + std::string GetFailureMsg() override { return "[FunctionPointerConverter]"; }; protected: std::string fRetType; @@ -436,7 +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]"; }; + std::string GetFailureMsg() override { return "[StdFunctionConverter]"; }; protected: Converter* fConverter; @@ -446,8 +447,8 @@ class StdFunctionConverter : public FunctionPointerConverter { // smart pointer converter class SmartPtrConverter : public Converter { public: - SmartPtrConverter(Cppyy::TCppType_t smart, - Cppyy::TCppType_t underlying, + SmartPtrConverter(Cppyy::TCppScope_t smart, + Cppyy::TCppScope_t underlying, bool keepControl = false, bool isRef = false) : fSmartPtrType(smart), fUnderlyingType(underlying), @@ -458,13 +459,13 @@ 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]"; }; + std::string GetFailureMsg() override { return "[SmartPtrConverter]"; }; protected: virtual bool GetAddressSpecialCase(PyObject*, void*&) { return false; } - Cppyy::TCppType_t fSmartPtrType; - Cppyy::TCppType_t fUnderlyingType; + Cppyy::TCppScope_t fSmartPtrType; + Cppyy::TCppScope_t fUnderlyingType; bool fKeepControl; bool fIsRef; }; @@ -473,7 +474,7 @@ class SmartPtrConverter : public Converter { // initializer lists class InitializerListConverter : public InstanceConverter { public: - InitializerListConverter(Cppyy::TCppType_t klass, std::string const& value_type); + InitializerListConverter(Cppyy::TCppScope_t klass, std::string const& value_type); InitializerListConverter(const InitializerListConverter&) = delete; InitializerListConverter& operator=(const InitializerListConverter&) = delete; virtual ~InitializerListConverter(); @@ -481,7 +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]"; }; + std::string GetFailureMsg() override { return "[FunctionPointerConverter]"; }; protected: void Clear(); @@ -490,7 +491,7 @@ class InitializerListConverter : public InstanceConverter { void* fBuffer = nullptr; std::vector fConverters; std::string fValueTypeName; - Cppyy::TCppType_t fValueType; + Cppyy::TCppScope_t fValueType; size_t fValueSize; }; diff --git a/src/CPyCppyy/src/DeclareExecutors.h b/src/CPyCppyy/src/DeclareExecutors.h index a092061..19dea66 100644 --- a/src/CPyCppyy/src/DeclareExecutors.h +++ b/src/CPyCppyy/src/DeclareExecutors.h @@ -1,6 +1,9 @@ #ifndef CPYCPPYY_DECLAREEXECUTORS_H #define CPYCPPYY_DECLAREEXECUTORS_H +#include "Python.h" +#include "Cppyy.h" + // Bindings #include "Executors.h" #include "CallContext.h" @@ -32,7 +35,9 @@ CPPYY_DECL_EXEC(WChar); CPPYY_DECL_EXEC(Char16); CPPYY_DECL_EXEC(Char32); CPPYY_DECL_EXEC(Int8); +CPPYY_DECL_EXEC(Int8ConstRef); CPPYY_DECL_EXEC(UInt8); +CPPYY_DECL_EXEC(UInt8ConstRef); CPPYY_DECL_EXEC(Short); CPPYY_DECL_EXEC(Int); CPPYY_DECL_EXEC(Long); @@ -89,7 +94,7 @@ CPPYY_DECL_EXEC(STLWString); class InstancePtrExecutor : public Executor { public: - InstancePtrExecutor(Cppyy::TCppType_t klass) : fClass(klass) {} + InstancePtrExecutor(Cppyy::TCppScope_t klass) : fClass(klass) {} PyObject* Execute( Cppyy::TCppMethod_t, Cppyy::TCppObject_t, CallContext*) override; bool HasState() override { return true; } diff --git a/src/CPyCppyy/src/Dimensions.h b/src/CPyCppyy/src/Dimensions.h index c1bdc60..1f29ae0 100644 --- a/src/CPyCppyy/src/Dimensions.h +++ b/src/CPyCppyy/src/Dimensions.h @@ -1,6 +1,9 @@ #ifndef CPYCPPYY_DIMENSIONS_H #define CPYCPPYY_DIMENSIONS_H +#include "Python.h" +#include "Cppyy.h" + // Bindings #include "CPyCppyy/CommonDefs.h" diff --git a/src/CPyCppyy/src/Dispatcher.cxx b/src/CPyCppyy/src/Dispatcher.cxx index b4c6754..12b8ee4 100644 --- a/src/CPyCppyy/src/Dispatcher.cxx +++ b/src/CPyCppyy/src/Dispatcher.cxx @@ -2,6 +2,7 @@ #include "CPyCppyy.h" #include "Dispatcher.h" #include "CPPScope.h" +#include "Cppyy.h" #include "PyStrings.h" #include "ProxyWrappers.h" #include "TypeManip.h" @@ -59,11 +60,7 @@ static inline void InjectMethod(Cppyy::TCppMethod_t method, const std::string& m Utility::ConstructCallbackPreamble(retType, argtypes, code); // perform actual method call -#if PY_VERSION_HEX < 0x03000000 - code << " PyObject* mtPyName = PyString_FromString(\"" << mtCppName << "\");\n" // TODO: intern? -#else code << " PyObject* mtPyName = PyUnicode_FromString(\"" << mtCppName << "\");\n" -#endif " PyObject* pyresult = PyObject_CallMethodObjArgs(iself, mtPyName"; for (Cppyy::TCppIndex_t i = 0; i < nArgs; ++i) code << ", pyargs[" << i << "]"; @@ -76,9 +73,9 @@ static inline void InjectMethod(Cppyy::TCppMethod_t method, const std::string& m //---------------------------------------------------------------------------- namespace { struct BaseInfo { - BaseInfo(Cppyy::TCppType_t t, std::string&& bn, std::string&& bns) : + BaseInfo(Cppyy::TCppScope_t t, std::string&& bn, std::string&& bns) : btype(t), bname(bn), bname_scoped(bns) {} - Cppyy::TCppType_t btype; + Cppyy::TCppScope_t btype; std::string bname; std::string bname_scoped; }; @@ -188,7 +185,7 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, if (!CPPScope_Check(PyTuple_GET_ITEM(bases, ibase))) continue; - Cppyy::TCppType_t basetype = ((CPPScope*)PyTuple_GET_ITEM(bases, ibase))->fCppType; + Cppyy::TCppScope_t basetype = ((CPPScope*)PyTuple_GET_ITEM(bases, ibase))->fCppType; if (!basetype) { err << "base class is incomplete"; @@ -301,7 +298,7 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, continue; } - std::string mtCppName = Cppyy::GetMethodName(method); + std::string mtCppName = Cppyy::GetName(Cppyy::TCppScope_t(method.data)); PyObject* key = CPyCppyy_PyText_FromString(mtCppName.c_str()); int contains = PyDict_Contains(dct, key); if (contains == -1) PyErr_Clear(); diff --git a/src/CPyCppyy/src/Executors.cxx b/src/CPyCppyy/src/Executors.cxx index fafc222..ce86a9d 100644 --- a/src/CPyCppyy/src/Executors.cxx +++ b/src/CPyCppyy/src/Executors.cxx @@ -21,7 +21,7 @@ //- data _____________________________________________________________________ namespace CPyCppyy { - typedef std::map ExecFactories_t; + typedef std::unordered_map ExecFactories_t; static ExecFactories_t gExecFactories; extern PyObject* gNullPtrObject; @@ -197,6 +197,20 @@ PyObject* CPyCppyy::UCharConstRefExecutor::Execute( return CPyCppyy_PyText_FromLong(*((unsigned char*)GILCallR(method, self, ctxt))); } +//---------------------------------------------------------------------------- +PyObject *CPyCppyy::Int8ConstRefExecutor::Execute(Cppyy::TCppMethod_t method, + Cppyy::TCppObject_t self, + CallContext *ctxt) { + return PyInt_FromLong(*((int8_t *)GILCallR(method, self, ctxt))); +} + +//---------------------------------------------------------------------------- +PyObject *CPyCppyy::UInt8ConstRefExecutor::Execute(Cppyy::TCppMethod_t method, + Cppyy::TCppObject_t self, + CallContext *ctxt) { + return PyInt_FromLong(*((uint8_t *)GILCallR(method, self, ctxt))); +} + //---------------------------------------------------------------------------- PyObject* CPyCppyy::WCharExecutor::Execute( Cppyy::TCppMethod_t method, Cppyy::TCppObject_t self, CallContext* ctxt) @@ -547,7 +561,7 @@ PyObject* CPyCppyy::Complex##code##Executor::Execute( \ { \ static Cppyy::TCppScope_t scopeid = Cppyy::GetScope("std::complex<"#type">");\ std::complex* result = \ - (std::complex*)GILCallO(method, self, ctxt, scopeid); \ + (std::complex*)GILCallO(method, self, ctxt, scopeid).data; \ if (!result) { \ PyErr_SetString(PyExc_ValueError, "NULL result where temporary expected");\ return nullptr; \ @@ -568,7 +582,7 @@ PyObject* CPyCppyy::STLStringExecutor::Execute( // TODO: make use of GILLCallS (?!) static Cppyy::TCppScope_t sSTLStringScope = Cppyy::GetFullScope("std::string"); - std::string* result = (std::string*)GILCallO(method, self, ctxt, sSTLStringScope); + std::string* result = (std::string*)GILCallO(method, self, ctxt, sSTLStringScope).data; if (!result) result = new std::string{}; else if (PyErr_Occurred()) { @@ -585,7 +599,7 @@ PyObject* CPyCppyy::STLWStringExecutor::Execute( { // execute with argument , construct python string return value static Cppyy::TCppScope_t sSTLWStringScope = Cppyy::GetFullScope("std::wstring"); - std::wstring* result = (std::wstring*)GILCallO(method, self, ctxt, sSTLWStringScope); + std::wstring* result = (std::wstring*)GILCallO(method, self, ctxt, sSTLWStringScope).data; if (!result) { wchar_t w = L'\0'; return PyUnicode_FromWideChar(&w, 0); @@ -757,7 +771,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::TCppScope_t)klass, ctxt); + return (PyObject*)GILCallConstructor(method, Cppyy::TCppScope_t(klass.data), ctxt).data; } //---------------------------------------------------------------------------- @@ -852,7 +866,7 @@ CPyCppyy::Executor* CPyCppyy::CreateExecutor(const std::string& fullType, cdims_ // C++ classes and special cases Executor* result = 0; - if (Cppyy::TCppType_t klass = Cppyy::GetFullScope(realType)) { + if (Cppyy::TCppScope_t klass = Cppyy::GetFullScope(realType)) { if (Utility::IsSTLIterator(realType) || gIteratorTypes.find(fullType) != gIteratorTypes.end()) { if (cpd == "") return new IteratorExecutor(klass); @@ -1120,10 +1134,16 @@ struct InitExecFactories_t { gf["char32_t"] = (ef_t)+[](cdims_t) { static Char32Executor e{}; return &e; }; gf["int8_t"] = (ef_t)+[](cdims_t) { static Int8Executor e{}; return &e; }; gf["int8_t&"] = (ef_t)+[](cdims_t) { return new Int8RefExecutor{}; }; - gf["const int8_t&"] = (ef_t)+[](cdims_t) { static Int8RefExecutor e{}; return &e; }; + gf["const int8_t&"] = (ef_t) + [](cdims_t) { + static Int8ConstRefExecutor e{}; + return &e; + }; gf["uint8_t"] = (ef_t)+[](cdims_t) { static UInt8Executor e{}; return &e; }; gf["uint8_t&"] = (ef_t)+[](cdims_t) { return new UInt8RefExecutor{}; }; - gf["const uint8_t&"] = (ef_t)+[](cdims_t) { static UInt8RefExecutor e{}; return &e; }; + gf["const uint8_t&"] = (ef_t) + [](cdims_t) { + static UInt8ConstRefExecutor e{}; + return &e; + }; gf["short"] = (ef_t)+[](cdims_t) { static ShortExecutor e{}; return &e; }; gf["short&"] = (ef_t)+[](cdims_t) { return new ShortRefExecutor{}; }; gf["int"] = (ef_t)+[](cdims_t) { static IntExecutor e{}; return &e; }; diff --git a/src/CPyCppyy/src/Executors.h b/src/CPyCppyy/src/Executors.h index 50eab0a..c081a43 100644 --- a/src/CPyCppyy/src/Executors.h +++ b/src/CPyCppyy/src/Executors.h @@ -1,6 +1,9 @@ #ifndef CPYCPPYY_EXECUTORS_H #define CPYCPPYY_EXECUTORS_H +#include "Python.h" +#include "Cppyy.h" + // Bindings #include "Dimensions.h" diff --git a/src/CPyCppyy/src/LowLevelViews.cxx b/src/CPyCppyy/src/LowLevelViews.cxx index e51015f..870a992 100644 --- a/src/CPyCppyy/src/LowLevelViews.cxx +++ b/src/CPyCppyy/src/LowLevelViews.cxx @@ -323,11 +323,7 @@ static inline int init_slice(Py_buffer* base, PyObject* _key, int dim) { Py_ssize_t start, stop, step, slicelength; -#if PY_VERSION_HEX < 0x03000000 - PySliceObject* key = (PySliceObject*)_key; -#else PyObject* key = _key; -#endif if (PySlice_GetIndicesEx(key, base->shape[dim], &start, &stop, &step, &slicelength) < 0) return -1; @@ -610,27 +606,6 @@ static int ll_ass_sub(CPyCppyy::LowLevelView* self, PyObject* key, PyObject* val return -1; } -#if PY_VERSION_HEX < 0x03000000 -//--------------------------------------------------------------------------- -static Py_ssize_t ll_oldgetbuf(CPyCppyy::LowLevelView* self, Py_ssize_t seg, void** pptr) -{ - if (seg != 0) { - PyErr_SetString(PyExc_TypeError, "accessing non-existent segment"); - return -1; - } - - *pptr = self->get_buf(); - return self->fBufInfo.len; -} - -//--------------------------------------------------------------------------- -static Py_ssize_t ll_getsegcount(PyObject*, Py_ssize_t* lenp) -{ - if (lenp) *lenp = 1; - return 1; -} -#endif - //--------------------------------------------------------------------------- static int ll_getbuf(CPyCppyy::LowLevelView* self, Py_buffer* view, int flags) { @@ -721,12 +696,6 @@ static PySequenceMethods ll_as_sequence = { //- buffer methods ---------------------------------------------------------- static PyBufferProcs ll_as_buffer = { -#if PY_VERSION_HEX < 0x03000000 - (readbufferproc)ll_oldgetbuf, // bf_getreadbuffer - (writebufferproc)ll_oldgetbuf, // bf_getwritebuffer - (segcountproc)ll_getsegcount, // bf_getsegcount - 0, // bf_getcharbuffer -#endif (getbufferproc)ll_getbuf, // bf_getbuffer 0, // bf_releasebuffer }; @@ -967,25 +936,12 @@ PyTypeObject LowLevelView_Type = { 0, // tp_mro 0, // tp_cache 0, // tp_subclasses - 0 // tp_weaklist -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, // tp_weaklist + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; } // namespace CPyCppyy diff --git a/src/CPyCppyy/src/MemoryRegulator.cxx b/src/CPyCppyy/src/MemoryRegulator.cxx index 52d8261..4b6d22f 100644 --- a/src/CPyCppyy/src/MemoryRegulator.cxx +++ b/src/CPyCppyy/src/MemoryRegulator.cxx @@ -1,4 +1,5 @@ // Bindings +#include "Cppyy.h" #include "CPyCppyy.h" #include "MemoryRegulator.h" #include "CPPInstance.h" @@ -35,18 +36,6 @@ 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 @@ -64,10 +53,6 @@ struct InitCPyCppyy_NoneType_t { CPyCppyy_NoneType.tp_dealloc = (destructor)&InitCPyCppyy_NoneType_t::DeAlloc; CPyCppyy_NoneType.tp_repr = Py_TYPE(Py_None)->tp_repr; CPyCppyy_NoneType.tp_richcompare = (richcmpfunc)&InitCPyCppyy_NoneType_t::RichCompare; -#if PY_VERSION_HEX < 0x03000000 - // tp_compare has become tp_reserved (place holder only) in p3 - CPyCppyy_NoneType.tp_compare = (cmpfunc)&InitCPyCppyy_NoneType_t::Compare; -#endif CPyCppyy_NoneType.tp_hash = (hashfunc)&InitCPyCppyy_NoneType_t::PtrHash; CPyCppyy_NoneType.tp_as_mapping = &CPyCppyy_NoneType_mapping; @@ -83,12 +68,8 @@ struct InitCPyCppyy_NoneType_t { } static int Compare(PyObject*, PyObject* other) { -#if PY_VERSION_HEX < 0x03000000 - return PyObject_Compare(other, Py_None); -#else // TODO the following isn't correct as it doesn't order, but will do for now ... return !PyObject_RichCompareBool(other, Py_None, Py_EQ); -#endif } }; @@ -109,7 +90,7 @@ CPyCppyy::MemoryRegulator::MemoryRegulator() //- public members ----------------------------------------------------------- bool CPyCppyy::MemoryRegulator::RecursiveRemove( - Cppyy::TCppObject_t cppobj, Cppyy::TCppType_t klass) + Cppyy::TCppObject_t cppobj, Cppyy::TCppScope_t klass) { // if registered by the framework, called whenever a cppobj gets destroyed if (!cppobj) diff --git a/src/CPyCppyy/src/MemoryRegulator.h b/src/CPyCppyy/src/MemoryRegulator.h index 2cd449a..6096f37 100644 --- a/src/CPyCppyy/src/MemoryRegulator.h +++ b/src/CPyCppyy/src/MemoryRegulator.h @@ -1,6 +1,9 @@ #ifndef CPYCPPYY_MEMORYREGULATOR_H #define CPYCPPYY_MEMORYREGULATOR_H +#include "Python.h" +#include "Cppyy.h" + #include #include @@ -8,7 +11,7 @@ namespace CPyCppyy { class CPPInstance; -typedef std::function(Cppyy::TCppObject_t, Cppyy::TCppType_t)> MemHook_t; +typedef std::function(Cppyy::TCppObject_t, Cppyy::TCppScope_t)> MemHook_t; class MemoryRegulator { private: @@ -18,10 +21,10 @@ class MemoryRegulator { MemoryRegulator(); // callback from C++-side frameworks - static bool RecursiveRemove(Cppyy::TCppObject_t cppobj, Cppyy::TCppType_t klass); + static bool RecursiveRemove(Cppyy::TCppObject_t cppobj, Cppyy::TCppScope_t klass); // called when a new python proxy object is created - static bool RegisterPyObject(CPPInstance* pyobj, void* cppobj); + static bool RegisterPyObject(CPPInstance* pyobj, Cppyy::TCppObject_t cppobj); // called when a the python proxy object is about to be garbage collected or when it is // about to delete the proxied C++ object, if owned diff --git a/src/CPyCppyy/src/ProxyWrappers.cxx b/src/CPyCppyy/src/ProxyWrappers.cxx index 33b6441..e03a4d2 100644 --- a/src/CPyCppyy/src/ProxyWrappers.cxx +++ b/src/CPyCppyy/src/ProxyWrappers.cxx @@ -12,6 +12,7 @@ #include "CPPOperator.h" #include "CPPOverload.h" #include "CPPScope.h" +#include "Cppyy.h" #include "MemoryRegulator.h" #include "PyStrings.h" #include "Pythonize.h" @@ -24,7 +25,7 @@ #include #include #include -#include +#include #include #include @@ -33,11 +34,11 @@ namespace CPyCppyy { extern PyObject* gThisModule; extern PyObject* gPyTypeMap; - extern std::set gPinnedTypes; + extern std::unordered_set gPinnedTypes; } // to prevent having to walk scopes, track python classes by C++ class -typedef std::map PyClassMap_t; +typedef std::unordered_map PyClassMap_t; static PyClassMap_t gPyClasses; @@ -169,11 +170,11 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons bool isNamespace = Cppyy::IsNamespace(scope); bool isComplete = Cppyy::IsComplete(scope); bool hasConstructor = false; - Cppyy::TCppMethod_t potGetItem = (Cppyy::TCppMethod_t)0; + Cppyy::TCppMethod_t potGetItem = nullptr; // load all public methods and data members typedef std::vector Callables_t; - typedef std::map CallableCache_t; + typedef std::unordered_map CallableCache_t; CallableCache_t cache; // bypass custom __getattr__ for efficiency @@ -192,7 +193,7 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons continue; // process the method based on its name - std::string mtCppName = Cppyy::GetMethodName(method); + std::string mtCppName = Cppyy::GetName(Cppyy::TCppScope_t(method.data)); // special case trackers bool setupSetItem = false; @@ -307,7 +308,7 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons std::vector templ_methods; Cppyy::GetTemplatedMethods(scope, templ_methods); for (auto &method : templ_methods) { - const std::string mtCppName = Cppyy::GetMethodName(method); + const std::string mtCppName = Cppyy::GetName(Cppyy::TCppScope_t(method.data)); // 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 @@ -330,13 +331,13 @@ static int BuildScopeProxyDict(Cppyy::TCppScope_t scope, PyObject* pyclass, cons PyCallable* defctor = nullptr; if (!isComplete) { ((CPPScope*)pyclass)->fFlags |= CPPScope::kIsInComplete; - defctor = new CPPIncompleteClassConstructor(scope, (Cppyy::TCppMethod_t)0); + defctor = new CPPIncompleteClassConstructor(scope, Cppyy::TCppMethod_t{}); } else if (Cppyy::IsAbstract(scope)) { - defctor = new CPPAbstractClassConstructor(scope, (Cppyy::TCppMethod_t)0); + defctor = new CPPAbstractClassConstructor(scope, Cppyy::TCppMethod_t{}); } else if (isNamespace) { - defctor = new CPPNamespaceConstructor(scope, (Cppyy::TCppMethod_t)0); + defctor = new CPPNamespaceConstructor(scope, Cppyy::TCppMethod_t{}); } else { - defctor = new CPPAllPrivateClassConstructor(scope, (Cppyy::TCppMethod_t)0); + defctor = new CPPAllPrivateClassConstructor(scope, Cppyy::TCppMethod_t{}); } if (defctor) @@ -554,7 +555,7 @@ 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 - Cppyy::TCppScope_t parent_scope = 0; + Cppyy::TCppScope_t parent_scope; if (parent) { if (CPPScope_Check(parent)) parent_scope = ((CPPScope*)parent)->fCppType; @@ -630,7 +631,7 @@ PyObject* CPyCppyy::CreateScopeProxy(Cppyy::TCppScope_t scope, PyObject* parent, std::string typedefed_name = ""; if (Cppyy::IsTypedefed(scope)) { is_typedef = true; - typedefed_name = Cppyy::GetMethodFullName(scope); + typedefed_name = Cppyy::GetFullName(scope); Cppyy::TCppScope_t underlying_scope = Cppyy::GetUnderlyingScope(scope); if ((underlying_scope) && (underlying_scope != scope)) { scope = underlying_scope; @@ -647,7 +648,7 @@ PyObject* CPyCppyy::CreateScopeProxy(Cppyy::TCppScope_t scope, PyObject* parent, 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* cppscope = PyLong_FromVoidPtr(scope.data); PyObject* pytemplate = PyObject_CallFunction( pytcl, const_cast("sO"), const_cast(Cppyy::GetScopedFinalName(scope).c_str()), @@ -836,13 +837,35 @@ PyObject* CPyCppyy::BindCppObjectNoCast(Cppyy::TCppObject_t address, bool noReg = flags & (CPPInstance::kNoMemReg|CPPInstance::kNoWrapConv); bool isRef = flags & CPPInstance::kIsReference; - void* r_address = isRef ? (address ? *(void**)address : nullptr) : address; + void* r_address = isRef ? (address ? *(void**)address.data : nullptr) : address.data; // check whether the object to be bound is a smart pointer that needs embedding PyObject* smart_type = (!(flags & CPPInstance::kNoWrapConv) && \ (((CPPClass*)pyclass)->fFlags & CPPScope::kIsSmart)) ? pyclass : nullptr; if (smart_type) { - pyclass = CreateScopeProxy(((CPPSmartClass*)smart_type)->fUnderlyingType); + Cppyy::TCppScope_t underlying = ((CPPSmartClass*)smart_type)->fUnderlyingType; + + // Downcast the underlying object to its actual (most derived) class, as + // BindCppObject does for raw pointers, but only when the cast is safe: + // * the actual class must really derive from the declared one; + // GetActualClass() can report a base (e.g. when the actual class has no + // dictionary of its own), and such an up-cast must be rejected. + // * the cast must need no pointer adjustment: the dereferencer always + // returns a pointer to the declared class, so a non-zero offset could + // not be applied consistently on later member access (multiple + // inheritance). + if (address && !isRef) { + if (void* deref = Cppyy::CallR( + ((CPPSmartClass*)smart_type)->fDereferencer, address, 0, nullptr)) { + Cppyy::TCppScope_t clActual = Cppyy::GetActualClass(underlying, deref); + if (clActual && clActual != underlying && + Cppyy::IsSubclass(clActual, underlying) && + Cppyy::GetBaseOffset(clActual, underlying, deref, -1 /* down-cast */) == 0) + underlying = clActual; + } + } + + pyclass = CreateScopeProxy(underlying); if (!pyclass) { // simply restore and expose as the actual smart pointer class pyclass = smart_type; @@ -883,7 +906,7 @@ PyObject* CPyCppyy::BindCppObjectNoCast(Cppyy::TCppObject_t address, if (pyobj != 0) { // fill proxy value? unsigned objflags = flags & \ (CPPInstance::kIsReference | CPPInstance::kIsPtrPtr | CPPInstance::kIsValue | CPPInstance::kIsOwner | CPPInstance::kIsActual); - pyobj->Set(address, (CPPInstance::EFlags)objflags); + pyobj->Set(address.data, (CPPInstance::EFlags)objflags); if (smart_type) pyobj->SetSmart(smart_type); @@ -928,14 +951,14 @@ PyObject* CPyCppyy::BindCppObject(Cppyy::TCppObject_t address, // TODO: optimize for final classes unsigned new_flags = flags; if (!isRef && (gPinnedTypes.empty() || gPinnedTypes.find(klass) == gPinnedTypes.end())) { - Cppyy::TCppType_t clActual = Cppyy::GetActualClass(klass, address); + Cppyy::TCppScope_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); + address = (void*)((intptr_t)address.data + offset); klass = clActual; } } diff --git a/src/CPyCppyy/src/PyObjectDir27.inc b/src/CPyCppyy/src/PyObjectDir27.inc index 3274a75..88f2762 100644 --- a/src/CPyCppyy/src/PyObjectDir27.inc +++ b/src/CPyCppyy/src/PyObjectDir27.inc @@ -111,57 +111,6 @@ static int merge_class_dict(PyObject* dict, PyObject* aclass) return 0; } -#if PY_VERSION_HEX < 0x03000000 - -/* Helper for PyObject_Dir. - If obj has an attr named attrname that's a list, merge its string - elements into keys of dict. - Return 0 on success, -1 on error. Errors due to not finding the attr, - or the attr not being a list, are suppressed. -*/ - -static int -merge_list_attr(PyObject* dict, PyObject* obj, const char *attrname) -{ - PyObject *list; - int result = 0; - - assert(PyDict_Check(dict)); - assert(obj); - assert(attrname); - - list = PyObject_GetAttrString(obj, attrname); - if (list == NULL) - PyErr_Clear(); - - else if (PyList_Check(list)) { - int i; - for (i = 0; i < PyList_GET_SIZE(list); ++i) { - PyObject *item = PyList_GET_ITEM(list, i); - if (PyString_Check(item)) { - result = PyDict_SetItem(dict, item, Py_None); - if (result < 0) - break; - } - } - if (Py_Py3kWarningFlag && - (strcmp(attrname, "__members__") == 0 || - strcmp(attrname, "__methods__") == 0)) { - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "__members__ and __methods__ not " - "supported in 3.x", 1) < 0) { - Py_XDECREF(list); - return -1; - } - } - } - - Py_XDECREF(list); - return result; -} - -#endif - /* Helper for PyObject_Dir of type objects: returns __dict__ and __bases__. We deliberately don't suck up its __class__, as methods belonging to the metaclass would probably be more confusing than helpful. @@ -208,15 +157,6 @@ static PyObject* _generic_dir(PyObject *obj) if (dict == NULL) goto error; -#if PY_VERSION_HEX < 0x03000000 - /* Merge in __members__ and __methods__ (if any). - * This is removed in Python 3000. */ - if (merge_list_attr(dict, obj, "__members__") < 0) - goto error; - if (merge_list_attr(dict, obj, "__methods__") < 0) - goto error; -#endif - /* Merge in attrs reachable from its class. */ itsclass = PyObject_GetAttrString(obj, "__class__"); if (itsclass == NULL) diff --git a/src/CPyCppyy/src/PyStrings.cxx b/src/CPyCppyy/src/PyStrings.cxx index 6a188f7..7142d24 100644 --- a/src/CPyCppyy/src/PyStrings.cxx +++ b/src/CPyCppyy/src/PyStrings.cxx @@ -92,11 +92,7 @@ bool CPyCppyy::CreatePyStrings() { CPPYY_INITIALIZE_STRING(gBase, __base__); CPPYY_INITIALIZE_STRING(gContains, contains); CPPYY_INITIALIZE_STRING(gCopy, copy); -#if PY_VERSION_HEX < 0x03000000 - CPPYY_INITIALIZE_STRING(gCppBool, __cpp_nonzero__); -#else CPPYY_INITIALIZE_STRING(gCppBool, __cpp_bool__); -#endif CPPYY_INITIALIZE_STRING(gCppName, __cpp_name__); CPPYY_INITIALIZE_STRING(gAnnotations, __annotations__); CPPYY_INITIALIZE_STRING(gCastCpp, __cast_cpp__); diff --git a/src/CPyCppyy/src/Pythonize.cxx b/src/CPyCppyy/src/Pythonize.cxx index 77be37b..a4be700 100644 --- a/src/CPyCppyy/src/Pythonize.cxx +++ b/src/CPyCppyy/src/Pythonize.cxx @@ -27,7 +27,7 @@ //- data and local helpers --------------------------------------------------- namespace CPyCppyy { extern PyObject* gThisModule; - std::map> &pythonizations(); + std::unordered_map> &pythonizations(); } namespace { @@ -214,9 +214,6 @@ PyObject* NullCheckBool(PyObject* self) } //- vector behavior as primitives ---------------------------------------------- -#if PY_VERSION_HEX < 0x03040000 -#define PyObject_LengthHint _PyObject_LengthHint -#endif // TODO: can probably use the below getters in the InitializerListConverter struct ItemGetter { @@ -610,7 +607,7 @@ static PyObject* vector_iter(PyObject* v) { vi->vi_data = nullptr; vi->vi_stride = 0; vi->vi_converter = nullptr; - vi->vi_klass = 0; + vi->vi_klass = nullptr; vi->vi_flags = 0; } @@ -658,7 +655,7 @@ PyObject* VectorGetItem(CPPInstance* self, PySliceObject* index) } -static Cppyy::TCppType_t sVectorBoolTypeID = (Cppyy::TCppType_t)0; +static Cppyy::TCppScope_t sVectorBoolTypeID; PyObject* VectorBoolGetItem(CPPInstance* self, PyObject* idx) { @@ -850,13 +847,8 @@ PyObject* MapInit(PyObject* self, PyObject* args, PyObject* /* kwds */) if (PyTuple_GET_SIZE(args) == 1 && PyMapping_Check(PyTuple_GET_ITEM(args, 0)) && \ !(PyTuple_Check(PyTuple_GET_ITEM(args, 0)) || PyList_Check(PyTuple_GET_ITEM(args, 0)))) { PyObject* assoc = PyTuple_GET_ITEM(args, 0); -#if PY_VERSION_HEX < 0x03000000 - // to prevent warning about literal string, expand macro - PyObject* items = PyObject_CallMethod(assoc, (char*)"items", nullptr); -#else // in p3, PyMapping_Items isn't a macro, but a function that short-circuits dict PyObject* items = PyMapping_Items(assoc); -#endif if (items && PySequence_Check(items)) { PyObject* result = MapFromPairs(self, items); Py_DECREF(items); @@ -884,7 +876,6 @@ PyObject* MapInit(PyObject* self, PyObject* args, PyObject* /* kwds */) return nullptr; } -#if __cplusplus <= 202002L PyObject* STLContainsWithFind(PyObject* self, PyObject* obj) { // Implement python's __contains__ for std::map/std::set @@ -911,7 +902,6 @@ PyObject* STLContainsWithFind(PyObject* self, PyObject* obj) return result; } -#endif //- set behavior as primitives ------------------------------------------------ @@ -1117,12 +1107,10 @@ PyObject* SmartPtrInit(PyObject* self, PyObject* args, PyObject* /* kwds */) //- string behavior as primitives -------------------------------------------- -#if PY_VERSION_HEX >= 0x03000000 // TODO: this is wrong, b/c it doesn't order static int PyObject_Compare(PyObject* one, PyObject* other) { return !PyObject_RichCompareBool(one, other, Py_EQ); } -#endif static inline PyObject* CPyCppyy_PyString_FromCppString(std::string_view s, bool native=true) { if (native) @@ -1254,7 +1242,6 @@ PyObject* STLStringDecode(CPPInstance* self, PyObject* args, PyObject* kwds) return PyUnicode_Decode(obj->data(), obj->size(), encoding, errors); } -#if __cplusplus <= 202302L PyObject* STLStringContains(CPPInstance* self, PyObject* pyobj) { std::string* obj = GetSTLString(self); @@ -1271,7 +1258,6 @@ PyObject* STLStringContains(CPPInstance* self, PyObject* pyobj) Py_RETURN_FALSE; } -#endif PyObject* STLStringReplace(CPPInstance* self, PyObject* args, PyObject* /*kwds*/) { @@ -1628,11 +1614,7 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, Cppyy::TCppScope_t scope) // for pre-check of nullptr for boolean types if (HasAttrDirect(pyclass, PyStrings::gCppBool)) { -#if PY_VERSION_HEX >= 0x03000000 const char* pybool_name = "__bool__"; -#else - const char* pybool_name = "__nonzero__"; -#endif Utility::AddToClass(pyclass, pybool_name, (PyCFunction)NullCheckBool, METH_NOARGS); } @@ -1741,7 +1723,7 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, Cppyy::TCppScope_t scope) 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::TCppScope_t kls = ((CPPClass*)pyclass)->fCppType; std::vector datamems; Cppyy::GetDatamembers(kls, datamems); if (!datamems.empty()) { @@ -1821,7 +1803,7 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, Cppyy::TCppScope_t scope) if (IsTemplatedSTLClass(name, "vector")) { // std::vector is a special case in C++ - if (!sVectorBoolTypeID) sVectorBoolTypeID = (Cppyy::TCppType_t)Cppyy::GetScope("std::vector"); + if (!sVectorBoolTypeID) sVectorBoolTypeID = Cppyy::GetScope("std::vector"); if (klass->fCppType == sVectorBoolTypeID) { Utility::AddToClass(pyclass, "__getitem__", (PyCFunction)VectorBoolGetItem, METH_O); Utility::AddToClass(pyclass, "__setitem__", (PyCFunction)VectorBoolSetItem); @@ -1853,7 +1835,7 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, Cppyy::TCppScope_t scope) 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* pyvalue_type = PyLong_FromVoidPtr(vtype.data); PyObject_SetAttr(pyclass, PyStrings::gValueTypePtr, pyvalue_type); Py_DECREF(pyvalue_type); pyvalue_type = PyUnicode_FromString(Cppyy::GetTypeAsString(vtype).c_str()); @@ -1880,20 +1862,24 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, Cppyy::TCppScope_t scope) // constructor that takes python associative collections Utility::AddToClass(pyclass, "__real_init", "__init__"); Utility::AddToClass(pyclass, "__init__", (PyCFunction)MapInit, METH_VARARGS | METH_KEYWORDS); -#if __cplusplus <= 202002L - // From C++20, std::map and std::unordered_map already implement a contains() method. - Utility::AddToClass(pyclass, "__contains__", (PyCFunction)STLContainsWithFind, METH_O); -#endif + // From C++20, std::map/unordered_map have a native contains() that the generic + // contains->__contains__ mapping above will pick up. Strong-types reflection + // does not always expose it, so fall back to a find()-based __contains__ when + // none was reflected (still O(log n)/O(1), never iterating). + if (!HasAttrDirect(pyclass, PyStrings::gContains)) + Utility::AddToClass(pyclass, "__contains__", (PyCFunction)STLContainsWithFind, METH_O); } else if (IsTemplatedSTLClass(name, "set")) { // constructor that takes python associative collections Utility::AddToClass(pyclass, "__real_init", "__init__"); Utility::AddToClass(pyclass, "__init__", (PyCFunction)SetInit, METH_VARARGS | METH_KEYWORDS); -#if __cplusplus <= 202002L - // From C++20, std::set already implements a contains() method. - Utility::AddToClass(pyclass, "__contains__", (PyCFunction)STLContainsWithFind, METH_O); -#endif + // From C++20, std::set has a native contains() that the generic + // contains->__contains__ mapping above will pick up. Strong-types reflection + // does not always expose it, so fall back to a find()-based __contains__ when + // none was reflected (still O(log n), never iterating). + if (!HasAttrDirect(pyclass, PyStrings::gContains)) + Utility::AddToClass(pyclass, "__contains__", (PyCFunction)STLContainsWithFind, METH_O); } else if (IsTemplatedSTLClass(name, "pair")) { @@ -1923,10 +1909,11 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, Cppyy::TCppScope_t scope) Utility::AddToClass(pyclass, "__cmp__", (PyCFunction)STLStringCompare, METH_O); Utility::AddToClass(pyclass, "__eq__", (PyCFunction)STLStringIsEqual, METH_O); Utility::AddToClass(pyclass, "__ne__", (PyCFunction)STLStringIsNotEqual, METH_O); -#if __cplusplus <= 202302L - // From C++23, std::string already implements a contains() method. + // Python's `in` operator needs __contains__ on the proxy regardless of the + // C++ standard CPyCppyy is built with; it never dispatches to C++'s + // std::string::contains (C++23+). The old `__cplusplus <= 202302L` guard + // wrongly dropped it when built with -std=c++2c (__cplusplus == 202400L). Utility::AddToClass(pyclass, "__contains__", (PyCFunction)STLStringContains, METH_O); -#endif Utility::AddToClass(pyclass, "decode", (PyCFunction)STLStringDecode, METH_VARARGS | METH_KEYWORDS); Utility::AddToClass(pyclass, "__cpp_find", "find"); Utility::AddToClass(pyclass, "find", (PyCFunction)STLStringFind, METH_VARARGS | METH_KEYWORDS); diff --git a/src/CPyCppyy/src/TemplateProxy.cxx b/src/CPyCppyy/src/TemplateProxy.cxx index 6c31963..197d452 100644 --- a/src/CPyCppyy/src/TemplateProxy.cxx +++ b/src/CPyCppyy/src/TemplateProxy.cxx @@ -6,6 +6,7 @@ #include "CPPFunction.h" #include "CPPMethod.h" #include "CPPOverload.h" +#include "Cppyy.h" #include "PyCallable.h" #include "PyStrings.h" #include "Utility.h" @@ -81,7 +82,6 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname, // Instantiate (and cache) templated methods, return method if any std::string proto = ""; -#if PY_VERSION_HEX >= 0x03080000 // 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) && \ @@ -90,7 +90,6 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname, args += 1; nargsf -= 1; } -#endif Py_ssize_t argc = CPyCppyy_PyArgs_GET_SIZE(args, nargsf); if (argc != 0) { @@ -163,17 +162,12 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname, } } -#if PY_VERSION_HEX >= 0x03080000 PyObject* pyargs = PyTuple_New(argc); for (Py_ssize_t i = 0; i < argc; ++i) { PyObject* item = CPyCppyy_PyArgs_GET_ITEM(args, i); Py_INCREF(item); PyTuple_SET_ITEM(pyargs, i, item); } -#else - Py_INCREF(args); - PyObject* pyargs = args; -#endif const std::string& name_v1 = \ Utility::ConstructTemplateArgs(nullptr, tpArgs, pyargs, pref, 0, pcnt); @@ -205,7 +199,7 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname, // TODO: this caches the lookup method before the call, meaning that failing overloads // can add already existing overloads to the set of methods. - std::string resname = Cppyy::GetMethodFullName(cppmeth); + std::string resname = Cppyy::GetFullName(Cppyy::TCppScope_t(cppmeth.data)); // An initializer_list is preferred for the argument types, but should not leak into // the argument types. If it did, replace with vector and lookup anew. @@ -221,7 +215,7 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname, // replace if the new method with vector was found; otherwise just continue // with the previously found method with initializer_list. cppmeth = m2; - resname = Cppyy::GetMethodFullName(cppmeth); + resname = Cppyy::GetFullName(Cppyy::TCppScope_t(cppmeth.data)); } } @@ -513,12 +507,8 @@ static inline PyObject* CallMethodImp(TemplateProxy* pytmpl, PyObject*& pymeth, return result; } -#if PY_VERSION_HEX >= 0x03080000 static PyObject* tpp_vectorcall( TemplateProxy* pytmpl, PyObject* const *args, size_t nargsf, PyObject* kwds) -#else -static PyObject* tpp_call(TemplateProxy* pytmpl, PyObject* args, PyObject* kwds) -#endif { // Dispatcher to the actual member method, several uses possible; in order: // @@ -548,10 +538,6 @@ static PyObject* tpp_call(TemplateProxy* pytmpl, PyObject* args, PyObject* kwds) // TODO: should previously instantiated templates be considered first? -#if PY_VERSION_HEX < 0x03080000 - size_t nargsf = PyTuple_GET_SIZE(args); -#endif - PyObject *pymeth = nullptr, *result = nullptr; // short-cut through memoization map @@ -709,9 +695,7 @@ static TemplateProxy* tpp_descr_get(TemplateProxy* pytmpl, PyObject* pyobj, PyOb // copy name, class, etc. pointers new (&newPyTmpl->fTI) std::shared_ptr{pytmpl->fTI}; -#if PY_VERSION_HEX >= 0x03080000 newPyTmpl->fVectorCall = pytmpl->fVectorCall; -#endif return newPyTmpl; } @@ -777,9 +761,7 @@ void TemplateProxy::Set(const std::string& cppname, const std::string& pyname, P fTI->fTemplated = CPPOverload_New(pyname, dummy); fTI->fLowPriority = CPPOverload_New(pyname, dummy); -#if PY_VERSION_HEX >= 0x03080000 fVectorCall = (vectorcallfunc)tpp_vectorcall; -#endif } @@ -791,8 +773,8 @@ static PyObject* tpp_overload(TemplateProxy* pytmpl, PyObject* args) PyObject* sigarg_tuple = nullptr; int want_const = -1; - Cppyy::TCppScope_t scope = (Cppyy::TCppScope_t) 0; - Cppyy::TCppMethod_t cppmeth = (Cppyy::TCppMethod_t) 0; + Cppyy::TCppScope_t scope = nullptr; + Cppyy::TCppMethod_t cppmeth = nullptr; std::string proto; if (PyArg_ParseTuple(args, const_cast("s|i:__overload__"), &sigarg, &want_const)) { @@ -890,11 +872,7 @@ PyTypeObject TemplateProxy_Type = { sizeof(TemplateProxy), // tp_basicsize 0, // tp_itemsize (destructor)tpp_dealloc, // tp_dealloc -#if PY_VERSION_HEX >= 0x03080000 offsetof(TemplateProxy, fVectorCall), -#else - 0, // tp_vectorcall_offset / tp_print -#endif 0, // tp_getattr 0, // tp_setattr 0, // tp_as_async / tp_compare @@ -903,20 +881,13 @@ PyTypeObject TemplateProxy_Type = { 0, // tp_as_sequence &tpp_as_mapping, // tp_as_mapping (hashfunc)tpp_hash, // tp_hash -#if PY_VERSION_HEX >= 0x03080000 (ternaryfunc)PyVectorcall_Call, // tp_call -#else - (ternaryfunc)tpp_call, // tp_call -#endif 0, // tp_str 0, // tp_getattro 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC -#if PY_VERSION_HEX >= 0x03080000 - | Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_METHOD_DESCRIPTOR -#endif - , // tp_flags + | Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_METHOD_DESCRIPTOR, // tp_flags (char*)"cppyy template proxy (internal)", // tp_doc (traverseproc)tpp_traverse, // tp_traverse (inquiry)tpp_clear, // tp_clear @@ -941,25 +912,12 @@ PyTypeObject TemplateProxy_Type = { 0, // tp_mro 0, // tp_cache 0, // tp_subclasses - 0 // tp_weaklist -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, // tp_weaklist + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; } // namespace CPyCppyy diff --git a/src/CPyCppyy/src/TemplateProxy.h b/src/CPyCppyy/src/TemplateProxy.h index a286fe7..79c7402 100644 --- a/src/CPyCppyy/src/TemplateProxy.h +++ b/src/CPyCppyy/src/TemplateProxy.h @@ -22,7 +22,7 @@ class CPPOverload; */ typedef std::pair TP_DispatchEntry_t; -typedef std::map> TP_DispatchMap_t; +typedef std::unordered_map> TP_DispatchMap_t; class TemplateInfo { public: @@ -55,9 +55,7 @@ class TemplateProxy { PyObject* fSelf; // must be first (same layout as CPPOverload) PyObject* fTemplateArgs; PyObject* fWeakrefList; -#if PY_VERSION_HEX >= 0x03080000 vectorcallfunc fVectorCall; -#endif TP_TInfo_t fTI; public: diff --git a/src/CPyCppyy/src/TupleOfInstances.cxx b/src/CPyCppyy/src/TupleOfInstances.cxx index 622bc5d..e7308ab 100644 --- a/src/CPyCppyy/src/TupleOfInstances.cxx +++ b/src/CPyCppyy/src/TupleOfInstances.cxx @@ -1,3 +1,6 @@ +#include "Python.h" +#include "Cppyy.h" + // Bindings #include "CPyCppyy.h" #include "TupleOfInstances.h" @@ -8,7 +11,7 @@ namespace { typedef struct { PyObject_HEAD - Cppyy::TCppType_t ia_klass; + Cppyy::TCppScope_t ia_klass; void* ia_array_start; Py_ssize_t ia_pos; Py_ssize_t ia_len; @@ -104,25 +107,12 @@ PyTypeObject InstanceArrayIter_Type = { (iternextfunc)ia_iternext, // tp_iternext 0, 0, ia_getset, // tp_getset - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; @@ -137,7 +127,7 @@ PyObject* TupleOfInstances_New( if (!ia) return nullptr; ia->ia_klass = klass; - ia->ia_array_start = address; + ia->ia_array_start = address.data; ia->ia_pos = 0; ia->ia_len = -1; ia->ia_stride = Cppyy::SizeOf(klass); @@ -157,7 +147,7 @@ PyObject* TupleOfInstances_New( PyObject* tup = PyTuple_New(nelems); for (Py_ssize_t i = 0; i < nelems; ++i) { PyTuple_SetItem(tup, i, TupleOfInstances_New( - (char*)address + i*block_size, klass, dims.sub())); + ((char*)address.data) + i*block_size, klass, dims.sub())); } return tup; } else { @@ -178,7 +168,7 @@ PyObject* TupleOfInstances_New( // TODO: there's an assumption here that there is no padding, which is bound // to be incorrect in certain cases PyTuple_SetItem(tup, i, - BindCppObjectNoCast((char*)address + i*block_size, klass)); + BindCppObjectNoCast(((char*)address.data) + i*block_size, klass)); // Note: objects are bound as pointers, yet since the pointer value stays in // place, updates propagate just as if they were bound by-reference } @@ -244,25 +234,12 @@ PyTypeObject TupleOfInstances_Type = { 0, // tp_mro 0, // tp_cache 0, // tp_subclasses - 0 // tp_weaklist -#if PY_VERSION_HEX >= 0x02030000 - , 0 // tp_del -#endif -#if PY_VERSION_HEX >= 0x02060000 - , 0 // tp_version_tag -#endif -#if PY_VERSION_HEX >= 0x03040000 - , 0 // tp_finalize -#endif -#if PY_VERSION_HEX >= 0x03080000 - , 0 // tp_vectorcall -#endif -#if PY_VERSION_HEX >= 0x030c0000 - , 0 // tp_watched -#endif -#if PY_VERSION_HEX >= 0x030d0000 - , 0 // tp_versions_used -#endif + 0, // tp_weaklist + 0, // tp_del + 0, // tp_version_tag + 0, // tp_finalize + 0 // tp_vectorcall + CPYCPPYY_PYTYPE_TAIL }; } // namespace CPyCppyy diff --git a/src/CPyCppyy/src/Utility.cxx b/src/CPyCppyy/src/Utility.cxx index 4dbfd48..fbc8c17 100644 --- a/src/CPyCppyy/src/Utility.cxx +++ b/src/CPyCppyy/src/Utility.cxx @@ -28,7 +28,7 @@ bool CPyCppyy::gDictLookupActive = false; #endif -typedef std::map TC2POperatorMapping_t; +typedef std::unordered_map TC2POperatorMapping_t; static TC2POperatorMapping_t gC2POperatorMapping; static std::set gOpSkip; static std::set gOpRemove; @@ -114,11 +114,7 @@ namespace { gC2POperatorMapping["->"] = "__follow__"; // not an actual python operator gC2POperatorMapping["="] = "__assign__"; // id. -#if PY_VERSION_HEX < 0x03000000 - gC2POperatorMapping["bool"] = "__cpp_nonzero__"; -#else gC2POperatorMapping["bool"] = "__cpp_bool__"; -#endif } } initOperatorMapping_; @@ -415,10 +411,6 @@ static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg, if (tn == (PyObject*)&PyInt_Type) { if (arg) { -#if PY_VERSION_HEX < 0x03000000 - long l = PyInt_AS_LONG(arg); - tmpl_name.append((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(); @@ -431,45 +423,19 @@ static bool AddTypeName(std::string& tmpl_name, PyObject* tn, PyObject* arg, } else tmpl_name.append((ll < INT_MIN || INT_MAX < ll) ? \ ((ll < LONG_MIN || LONG_MAX < ll) ? "long long" : "long") : "int"); -#endif } else tmpl_name.append("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(); - tmpl_name.append("long"); // still out of range, will fail later - } else - tmpl_name.append("unsigned long long"); // since already failed long long - } else - tmpl_name.append((ll < LONG_MIN || LONG_MAX < ll) ? "long long" : "long"); - } else - tmpl_name.append("long"); - - return true; - } -#endif - if (tn == (PyObject*)&PyFloat_Type) { // special case for floats (Python-speak for double) if from argument (only) tmpl_name.append(arg ? "double" : "float"); return true; } -#if PY_VERSION_HEX < 0x03000000 - if (tn == (PyObject*)&PyString_Type) { -#else if (tn == (PyObject*)&PyUnicode_Type) { -#endif tmpl_name.append("std::string"); return true; } @@ -691,63 +657,33 @@ static bool AddTypeName(std::vector& types, PyObject* tn, 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 + types.push_back(Cppyy::GetType("int").data); // still out of range, will fail later } else - types.push_back(Cppyy::GetType("unsigned long long")); // since already failed long long + types.push_back(Cppyy::GetType("unsigned long long").data); // 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 + ((ll < LONG_MIN || LONG_MAX < ll) ? "long long" : "long") : "int").data); } else { - types.push_back(Cppyy::GetType("int")); + types.push_back(Cppyy::GetType("int").data); } 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")); + types.push_back(Cppyy::GetType(arg ? "double" : "float").data); 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)); + types.push_back(Cppyy::GetType("std::string", /* enable_slow_lookup */ true).data); return true; } @@ -758,7 +694,7 @@ static bool AddTypeName(std::vector& types, PyObject* tn, ArgPreference subpref = pref == kValue ? kValue : kPointer; if (AddTypeName(subtype, (PyObject*)Py_TYPE(item), item, subpref)) { subtype.append(">"); - types.push_back(Cppyy::GetType(subtype)); + types.push_back(Cppyy::GetType(subtype).data); } Py_DECREF(item); } @@ -785,7 +721,7 @@ static bool AddTypeName(std::vector& types, PyObject* tn, } } } - types.push_back(cpp_type); + types.push_back(cpp_type.data); return true; } @@ -793,7 +729,7 @@ static bool AddTypeName(std::vector& types, PyObject* tn, 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)); + types.push_back(Cppyy::GetType(CPyCppyy_PyText_AsString(tpName), /* enable_slow_lookup */ true).data); Py_DECREF(tpName); return true; @@ -835,7 +771,7 @@ static bool AddTypeName(std::vector& types, PyObject* tn, PyObject* tpName = PyObject_GetAttr(arg, PyStrings::gCppName); if (tpName) { - types.push_back(Cppyy::GetType(CPyCppyy_PyText_AsString(tpName), /* enable_slow_lookup */ true)); + types.push_back(Cppyy::GetType(CPyCppyy_PyText_AsString(tpName), /* enable_slow_lookup */ true).data); Py_DECREF(tpName); return true; } @@ -849,17 +785,17 @@ static bool AddTypeName(std::vector& types, PyObject* tn, if (Cppyy::IsEnumType(type)) { PyObject *value_int = PyNumber_Index(tn); if (!value_int) { - types.push_back(type); + types.push_back(type.data); PyErr_Clear(); } else { PyObject* pystr = PyObject_Str(tn); std::string num = CPyCppyy_PyText_AsString(pystr); - types.push_back({type, strdup(num.c_str())}); + types.push_back({type.data, strdup(num.c_str())}); Py_DECREF(pystr); Py_DECREF(value_int); } } else { - types.push_back(type); + types.push_back(type.data); } Py_DECREF(tpName); return true; @@ -877,7 +813,7 @@ static bool AddTypeName(std::vector& types, PyObject* tn, num = "1"; else if (num == "False") num = "0"; - types.push_back({Cppyy::GetType("int"), strdup(num.c_str())}); + types.push_back({Cppyy::GetType("int").data, strdup(num.c_str())}); Py_DECREF(pystr); return true; } @@ -1058,8 +994,8 @@ void CPyCppyy::Utility::ConstructCallbackReturn(const std::string& retType, int //---------------------------------------------------------------------------- -static std::map sStdFuncLookup; -static std::map sStdFuncMakerLookup; +static std::unordered_map sStdFuncLookup; +static std::unordered_map sStdFuncMakerLookup; PyObject* CPyCppyy::Utility::FuncPtr2StdFunction( const std::string& retType, const std::string& signature, void* address) { @@ -1136,15 +1072,12 @@ bool CPyCppyy::Utility::InitProxy(PyObject* module, PyTypeObject* pytype, const } //---------------------------------------------------------------------------- -std::map const &CPyCppyy::Utility::TypecodeMap() +std::unordered_map const &CPyCppyy::Utility::TypecodeMap() { // See https://docs.python.org/3/library/array.html#array.array - static std::map typecodeMap{ + static std::unordered_map typecodeMap{ {"char", 'b'}, {"unsigned char", 'B'}, -#if PY_VERSION_HEX < 0x03100000 - {"wchar_t", 'u'}, -#endif #if PY_VERSION_HEX >= 0x030d0000 {"Py_UCS4", 'w'}, #endif @@ -1234,24 +1167,15 @@ Py_ssize_t CPyCppyy::Utility::GetBuffer(PyObject* pyobject, char tc, int size, v PySequenceMethods* seqmeths = Py_TYPE(pyobject)->tp_as_sequence; if (seqmeths != 0 && bufprocs != 0 -#if PY_VERSION_HEX < 0x03000000 - && bufprocs->bf_getwritebuffer != 0 - && (*(bufprocs->bf_getsegcount))(pyobject, 0) == 1 -#else && bufprocs->bf_getbuffer != 0 -#endif ) { // get the buffer -#if PY_VERSION_HEX < 0x03000000 - Py_ssize_t buflen = (*(bufprocs->bf_getwritebuffer))(pyobject, 0, &buf); -#else Py_buffer bufinfo; (*(bufprocs->bf_getbuffer))(pyobject, &bufinfo, PyBUF_WRITABLE); buf = (char*)bufinfo.buf; Py_ssize_t buflen = bufinfo.len; PyBuffer_Release(&bufinfo); -#endif if (buf && check == true) { // determine buffer compatibility (use "buf" as a status flag) @@ -1416,14 +1340,8 @@ PyObject* CPyCppyy::Utility::PyErr_Occurred_WithGIL() // Re-acquire the GIL before calling PyErr_Occurred() in case it has been // released; note that the p2.2 code assumes that there are no callbacks in // C++ to python (or at least none returning errors). -#if PY_VERSION_HEX >= 0x02030000 PythonGILRAII python_gil_raii; PyObject* e = PyErr_Occurred(); -#else - if (PyThreadState_GET()) - return PyErr_Occurred(); - PyObject* e = 0; -#endif return e; } diff --git a/src/CPyCppyy/src/Utility.h b/src/CPyCppyy/src/Utility.h index 0640963..cc4c2a5 100644 --- a/src/CPyCppyy/src/Utility.h +++ b/src/CPyCppyy/src/Utility.h @@ -2,11 +2,13 @@ #define CPYCPPYY_UTILITY_H // Standard -#include +#include #include #include #include +#include "Python.h" +#include "Cppyy.h" namespace CPyCppyy { @@ -31,9 +33,9 @@ bool AddToClass(PyObject* pyclass, const char* label, PyCallable* pyfunc); // helpers for dynamically constructing operators PyCallable* FindUnaryOperator(PyObject* pyclass, const char* op); PyCallable* FindBinaryOperator(PyObject* left, PyObject* right, - const char* op, Cppyy::TCppScope_t scope = 0); + const char* op, Cppyy::TCppScope_t scope = Cppyy::TCppScope_t{}); PyCallable* FindBinaryOperator(const std::string& lcname, const std::string& rcname, - const char* op, Cppyy::TCppScope_t scope = 0, bool reverse = false); + const char* op, Cppyy::TCppScope_t scope = Cppyy::TCppScope_t{}, bool reverse = false); // helper for template classes and methods enum ArgPreference { kNone, kPointer, kReference, kValue }; @@ -48,11 +50,7 @@ inline PyObject* CT2CppName(PyObject* pytc, const char* cpd, bool allow_voidp) const std::string& name = CT2CppNameS(pytc, allow_voidp); if (!name.empty()) { if (name == "const char*") cpd = ""; -#if PY_VERSION_HEX < 0x03000000 - return PyString_FromString((std::string{name}+cpd).c_str()); -#else return PyUnicode_FromString((std::string{name}+cpd).c_str()); -#endif } return nullptr; } @@ -68,7 +66,7 @@ PyObject* FuncPtr2StdFunction(const std::string& retType, const std::string& sig // initialize proxy type objects bool InitProxy(PyObject* module, PyTypeObject* pytype, const char* name); -std::map const &TypecodeMap(); +std::unordered_map const &TypecodeMap(); // retrieve the memory buffer from pyobject, return buflength, tc (optional) is python // array.array type code, size is type size, buf will point to buffer, and if check is diff --git a/src/backend/clingwrapper.cxx b/src/backend/clingwrapper.cxx index ed86d72..60ce104 100644 --- a/src/backend/clingwrapper.cxx +++ b/src/backend/clingwrapper.cxx @@ -33,43 +33,6 @@ #include #include -// 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; - -// 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; - std::recursive_mutex InterOpMutex; // builtin types @@ -127,32 +90,6 @@ 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: -// 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); -// } -// }; - static inline void push_tokens_from_string(char *s, std::vector &tokens) { char *token = strtok(s, " "); @@ -173,7 +110,7 @@ bool is_integral(std::string& s) } class ApplicationStarter { - Cpp::TInterp_t Interp; + Cppyy::TInterp_t Interp; public: ApplicationStarter() { std::lock_guard Lock(InterOpMutex); @@ -185,7 +122,7 @@ class ApplicationStarter { } // Check if somebody already loaded CppInterOp and created an // interpreter for us. - if (auto * existingInterp = Cpp::GetInterpreter()) { + if (auto existingInterp = Cpp::GetInterpreter()) { Interp = existingInterp; } else { @@ -296,7 +233,7 @@ class ApplicationStarter { std::stringstream InterpPtrSS; InterpPtrSS << "#ifndef __CLING__\n" << "namespace cling { namespace runtime {\n" - << "void* gCling=(void*)" << static_cast(Interp) + << "void* gCling=(void*)" << Interp.data << ";\n }}\n" << "#endif \n"; Cpp::Process(InterpPtrSS.str().c_str()); @@ -330,25 +267,7 @@ class ApplicationStarter { } // unnamed namespace -// // local helpers ------------------------------------------------------------- -// static inline -// TClassRef& type_from_handle(Cppyy::TCppScope_t scope) -// { -// assert((ClassRefs_t::size_type)scope < g_classrefs.size()); -// return g_classrefs[(ClassRefs_t::size_type)scope]; -// } -// -// 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); -// } -// return (TFunction *) wrap->fTF; -// } -// +// local helpers ------------------------------------------------------------- static inline char* cppstring_to_cstring(const std::string& cppstr) { @@ -356,21 +275,8 @@ char* cppstring_to_cstring(const std::string& cppstr) memcpy(cstr, cppstr.c_str(), cppstr.size()+1); return cstr; } -// -// 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; -// } -// return false; -// } -// -// -// // direct interpreter access ------------------------------------------------- + +// direct interpreter access ------------------------------------------------- // Returns false on failure and true on success bool Cppyy::Compile(const std::string& code, bool silent) { @@ -379,12 +285,11 @@ bool Cppyy::Compile(const std::string& code, bool silent) return !Cpp::Declare(code.c_str(), silent); } -std::string Cppyy::ToString(TCppType_t klass, TCppObject_t obj) +std::string Cppyy::ToString(TCppScope_t klass, TCppObject_t obj) { std::lock_guard Lock(InterOpMutex); - if (klass && obj && !Cpp::IsNamespace((TCppScope_t)klass)) - return Cpp::ObjToString(Cpp::GetQualifiedCompleteName(klass).c_str(), - (void*)obj); + if (klass && obj && !Cpp::IsNamespace(klass)) + return Cpp::ObjToString(Cpp::GetQualifiedCompleteName(klass).c_str(), obj.data); return ""; } @@ -398,81 +303,6 @@ std::string Cppyy::ResolveName(const std::string& name) { } return ""; } -// // Fully resolve the given name to the final type name. -// -// // try memoized type cache, in case seen before -// TCppType_t klass = find_memoized(cppitem_name); -// if (klass) return GetScopedFinalName(klass); -// -// // remove global scope '::' if present -// std::string tclean = cppitem_name.compare(0, 2, "::") == 0 ? -// cppitem_name.substr(2, std::string::npos) : cppitem_name; -// -// // classes (most common) -// tclean = TClassEdit::CleanType(tclean.c_str()); -// if (tclean.empty() [> unknown, eg. an operator <]) return cppitem_name; -// -// // reduce [N] to [] -// if (tclean[tclean.size()-1] == ']') -// tclean = tclean.substr(0, tclean.rfind('[')) + "[]"; -// -// // remove __restrict and __restrict__ -// auto pos = tclean.rfind("__restrict"); -// if (pos != std::string::npos) -// tclean = tclean.substr(0, pos); -// -// if (tclean.compare(0, 9, "std::byte") == 0) -// return tclean; -// -// // check data types list (accept only builtins as typedefs will -// // otherwise not be resolved) -// if (IsBuiltin(tclean)) return tclean; -// -// // special case for enums -// if (IsEnum(cppitem_name)) -// return ResolveEnum(cppitem_name); -// -// // 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); -// -// 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; -// } -// -// 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; -// } -// -// // 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; -// } -// -// 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::ResolveEnumReferenceType(TCppType_t type) { std::lock_guard Lock(InterOpMutex); @@ -626,11 +456,11 @@ bool split_comma_saparated_types(const std::string& name, return true; } -Cpp::TCppScope_t GetEnumFromCompleteName(const std::string &name) { +Cppyy::TCppScope_t GetEnumFromCompleteName(const std::string &name) { std::string delim = "::"; size_t start = 0; size_t end = name.find(delim); - Cpp::TCppScope_t curr_scope = 0; + Cppyy::TCppScope_t curr_scope; while (end != std::string::npos) { curr_scope = Cpp::GetNamed(name.substr(start, end - start), curr_scope); start = end + delim.length(); @@ -685,7 +515,7 @@ bool Cppyy::AppendTypesSlow(const std::string& name, if (!type) type = Cpp::GetType(name); if (type) { - types.emplace_back(type); + types.emplace_back(type.data); return false; } return true; @@ -699,15 +529,24 @@ bool Cppyy::AppendTypesSlow(const std::string& name, if (!struct_count) Cpp::Declare(code.c_str(), /*silent=*/true); // initialize the trampoline - std::string var = "__Cppyy_s" + std::to_string(struct_count++); - if (!Cpp::Declare(("__Cppyy_AppendTypesSlow<" + resolved_name + "> " + var +";\n").c_str(), /*silent=*/true)) { - std::lock_guard Lock(InterOpMutex); - TCppType_t varN = - Cpp::GetVariableType(Cpp::GetNamed(var.c_str(), /*parent=*/nullptr)); - TCppScope_t instance_class = Cpp::GetScopeFromType(varN); - size_t oldSize = types.size(); - Cpp::GetClassTemplateInstantiationArgs(instance_class, types); - return oldSize == types.size(); + // The trampoline declares its variable in the global scope, so a name + // written relative to a parent (e.g. "vector" looked up in std) + // won't resolve. Try the name as given, then qualified by the parent. + std::vector candidates = {resolved_name}; + if (parent && parent != Cpp::GetGlobalScope() && + (Cppyy::IsNamespace(parent) || Cppyy::IsClass(parent))) + candidates.push_back(Cpp::GetQualifiedCompleteName(parent) + "::" + resolved_name); + + for (const std::string& candidate : candidates) { + std::string var = "__Cppyy_s" + std::to_string(struct_count++); + if (!Cpp::Declare(("__Cppyy_AppendTypesSlow<" + candidate + "> " + var + ";\n").c_str(), /*silent=*/true)) { + TCppType_t varN = + Cpp::GetVariableType(Cpp::GetNamed(var.c_str(), /*parent=*/nullptr)); + TCppScope_t instance_class = Cpp::GetScopeFromType(varN); + size_t oldSize = types.size(); + Cpp::GetClassTemplateInstantiationArgs(instance_class, types); + return oldSize == types.size(); + } } // We split each individual types based on , and resolve it @@ -736,11 +575,11 @@ bool Cppyy::AppendTypesSlow(const std::string& name, if (is_integral(i)) integral_value = strdup(i.c_str()); - if (Cpp::TCppScope_t scope = GetEnumFromCompleteName(i)) + if (TCppScope_t scope = GetEnumFromCompleteName(i)) if (Cpp::IsEnumConstant(scope)) integral_value = strdup(std::to_string(Cpp::GetEnumConstantValue(scope)).c_str()); - types.emplace_back(type, integral_value); + types.emplace_back(type.data, integral_value); } return false; } @@ -785,7 +624,7 @@ Cppyy::TCppType_t Cppyy::GetType(const std::string &name, bool enable_slow_looku std::string using_clause = "using " + id + " = __typeof__(" + name + ");\n"; if (!Cpp::Declare(using_clause.c_str(), /*silent=*/true)) { - TCppScope_t lookup = Cpp::GetNamed(id, 0); + TCppScope_t lookup = Cpp::GetNamed(id); TCppType_t lookup_ty = Cpp::GetTypeFromScope(lookup); return Cpp::GetCanonicalType(lookup_ty); } @@ -857,25 +696,23 @@ Cppyy::TCppScope_t Cppyy::GetScope(const std::string& name, // 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); - - std::string pure_name = name.substr(0, start); - Cppyy::TCppScope_t scope = Cpp::GetScope(pure_name, parent_scope); - if (!scope && (!parent_scope || parent_scope == Cpp::GetGlobalScope())) - scope = Cpp::GetScopeFromCompleteName(pure_name); - - if (Cppyy::IsTemplate(scope)) { - std::vector templ_params; - InterOpMutex.unlock(); // unlock to allow AppendTypesSlow - if (!Cppyy::AppendTypesSlow(params, templ_params)) { - std::lock_guard Lock(InterOpMutex); - return Cpp::InstantiateTemplate(scope, templ_params.data(), - templ_params.size(), - /*instantiate_body=*/false); - } + // Templated type; may need instantiation. Resolve the whole type + // expression (e.g. "std::array") and read back its scope. + // Splitting off the argument list and resolving it directly cannot + // represent non-type arguments such as the `3` in std::array. + std::vector types; + InterOpMutex.unlock(); // unlock to allow AppendTypesSlow + bool added_new_type = !Cppyy::AppendTypesSlow(name, types, /*parent=*/parent_scope); + std::lock_guard Lock(InterOpMutex); + if (added_new_type && types.size() == 1) { + TCppScope_t scope = Cpp::GetScopeFromType(types[0].m_Type); + // Naming the type as a template argument above does not instantiate + // it, so the specialization may still be declared-but-undefined. + // Force its definition: callers expect a complete scope, e.g. to + // walk its base classes. + if (scope) + Cpp::IsComplete(scope); + return scope; } } return nullptr; @@ -952,7 +789,7 @@ Cppyy::TCppScope_t Cppyy::GetActualClass(TCppScope_t klass, TCppObject_t obj) { if (!Cpp::IsClassPolymorphic(klass)) return klass; - const std::type_info *typ = &typeid(*(AutoCastRTTI *)obj); + const std::type_info *typ = &typeid(*(AutoCastRTTI *)obj.data); std::string mangled_name = typ->name(); std::string demangled_name = Cpp::Demangle(mangled_name); @@ -975,13 +812,6 @@ size_t Cppyy::SizeOfType(TCppType_t klass) return Cpp::GetSizeOfType(klass); } -// size_t Cppyy::SizeOf(const std::string& type_name) -// { -// TDataType* dt = gROOT->GetType(type_name.c_str()); -// if (dt) return dt->Size(); -// return SizeOf(GetScope(type_name)); -// } - bool Cppyy::IsBuiltin(const std::string& type_name) { static std::set s_builtins = @@ -1065,11 +895,6 @@ void release_args(Parameter* args, size_t nargs) { } } -// static inline -// bool is_ready(CallWrapper* wrap, bool is_direct) { -// return (!is_direct && wrap->fFaceptr.fGeneric) || (is_direct && wrap->fFaceptr.fDirect); -// } - static inline bool WrapperCall(Cppyy::TCppMethod_t method, size_t nargs, void* args_, void* self, void* result) { @@ -1109,7 +934,7 @@ static inline T CallT(Cppyy::TCppMethod_t method, Cppyy::TCppObject_t self, size_t nargs, void* args) { T t{}; - if (WrapperCall(method, nargs, args, (void*)self, &t)) + if (WrapperCall(method, nargs, args, self.data, &t)) return t; throw std::runtime_error("failed to resolve function"); return (T)-1; @@ -1131,7 +956,7 @@ rtype Cppyy::Call##typecode(TCppMethod_t method, TCppObject_t self, size_t nargs void Cppyy::CallV(TCppMethod_t method, TCppObject_t self, size_t nargs, void* args) { - if (!WrapperCall(method, nargs, args, (void*)self, nullptr)) + if (!WrapperCall(method, nargs, args, self.data, nullptr)) return /* TODO ... report error */; } @@ -1148,7 +973,7 @@ CPPYY_IMP_CALL(LD, long double ) void* Cppyy::CallR(TCppMethod_t method, TCppObject_t self, size_t nargs, void* args) { void* r = nullptr; - if (WrapperCall(method, nargs, args, (void*)self, &r)) + if (WrapperCall(method, nargs, args, self.data, &r)) return r; return nullptr; } @@ -1159,7 +984,7 @@ char* Cppyy::CallS( char* cstr = nullptr; // 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)) { + if (WrapperCall(method, nargs, args, self.data, (void*)cppresult)) { cstr = cppstring_to_cstring(*cppresult); *length = cppresult->size(); cppresult->std::string::~basic_string(); @@ -1187,16 +1012,16 @@ Cppyy::TCppObject_t Cppyy::CallO(TCppMethod_t method, TCppObject_t self, size_t nargs, void* args, TCppType_t result_type) { void* obj = ::operator new(Cppyy::SizeOfType(result_type)); - if (WrapperCall(method, nargs, args, self, obj)) + if (WrapperCall(method, nargs, args, self.data, obj)) return (TCppObject_t)obj; ::operator delete(obj); - return (TCppObject_t)0; + return TCppObject_t{}; } Cppyy::TCppFuncAddr_t Cppyy::GetFunctionAddress(TCppMethod_t method, bool check_enabled) { std::lock_guard Lock(InterOpMutex); - return (TCppFuncAddr_t) Cpp::GetFunctionAddress(method); + return Cpp::GetFunctionAddress(method); } @@ -1260,7 +1085,7 @@ bool Cppyy::IsEnumType(TCppType_t type) return Cpp::IsEnumType(type); } -bool Cppyy::IsAggregate(TCppType_t type) +bool Cppyy::IsAggregate(TCppScope_t type) { // Test if this type is a "plain old data" type return Cpp::IsAggregate(type); @@ -1362,22 +1187,21 @@ void Cppyy::GetAllCppNames(TCppScope_t scope, std::set& cppnames) Cpp::GetAllCppNames(scope, cppnames); } -// -// // class reflection information ---------------------------------------------- +// class reflection information ---------------------------------------------- std::vector Cppyy::GetUsingNamespaces(TCppScope_t scope) { std::lock_guard Lock(InterOpMutex); return Cpp::GetUsingNamespaces(scope); } -// // class reflection information ---------------------------------------------- -std::string Cppyy::GetFinalName(TCppType_t klass) +// class reflection information ---------------------------------------------- +std::string Cppyy::GetFinalName(TCppScope_t klass) { std::lock_guard Lock(InterOpMutex); return Cpp::GetCompleteName(Cpp::GetUnderlyingScope(klass)); } -std::string Cppyy::GetScopedFinalName(TCppType_t klass) +std::string Cppyy::GetScopedFinalName(TCppScope_t klass) { std::lock_guard Lock(InterOpMutex); return Cpp::GetQualifiedCompleteName(klass); @@ -1390,30 +1214,6 @@ bool Cppyy::HasVirtualDestructor(TCppScope_t scope) return Cpp::IsVirtualMethod(func); } -// bool Cppyy::HasComplexHierarchy(TCppType_t klass) -// { -// 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; -// } - Cppyy::TCppIndex_t Cppyy::GetNumBases(TCppScope_t klass) { // Get the total number of base classes that this class has. @@ -1451,7 +1251,7 @@ Cppyy::TCppIndex_t Cppyy::GetNumBasesLongestBranch(TCppScope_t klass) { return *std::max_element(num.begin(), num.end()) + 1; } -std::string Cppyy::GetBaseName(TCppType_t klass, TCppIndex_t ibase) +std::string Cppyy::GetBaseName(TCppScope_t klass, TCppIndex_t ibase) { std::lock_guard Lock(InterOpMutex); return Cpp::GetName(Cpp::GetBaseClass(klass, ibase)); @@ -1508,17 +1308,6 @@ bool Cppyy::GetSmartPtrInfo( return (!deref || *deref) && (!raw || *raw); } -// void Cppyy::AddSmartPtrType(const std::string& type_name) -// { -// gSmartPtrTypes.insert(ResolveName(type_name)); -// } -// -// void Cppyy::AddTypeReducer(const std::string& reducable, const std::string& reduced) -// { -// gInterpreter->AddTypeReducer(reducable, reduced); -// } - - // type offsets -------------------------------------------------------------- ptrdiff_t Cppyy::GetBaseOffset(TCppScope_t derived, TCppScope_t base, TCppObject_t address, int direction, bool rerror) @@ -1532,83 +1321,32 @@ ptrdiff_t Cppyy::GetBaseOffset(TCppScope_t derived, TCppScope_t base, return (ptrdiff_t)(direction < 0 ? -offset : offset); } - -// // method/function reflection information ------------------------------------ -// Cppyy::TCppIndex_t Cppyy::GetNumMethods(TCppScope_t scope, bool accept_namespace) -// { -// 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(), true [> silent <]); -// -// // now reload the methods -// return (TCppIndex_t)cr->GetListOfMethods(true)->GetSize(); -// } -// } -// return nMethods; -// } -// -// return (TCppIndex_t)0; // unknown class? -// } - +// method/function reflection information ------------------------------------ void Cppyy::GetClassMethods(TCppScope_t scope, std::vector &methods) { std::lock_guard Lock(InterOpMutex); Cpp::GetClassMethods(scope, methods); } -std::vector Cppyy::GetMethodsFromName( +std::vector Cppyy::GetMethodsFromName( TCppScope_t scope, const std::string& name) { std::lock_guard Lock(InterOpMutex); return Cpp::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) +std::string Cppyy::GetName(TCppScope_t method) { std::lock_guard Lock(InterOpMutex); return Cpp::GetName(method); } -std::string Cppyy::GetMethodFullName(TCppMethod_t method) +std::string Cppyy::GetFullName(TCppScope_t method) { std::lock_guard Lock(InterOpMutex); return Cpp::GetCompleteName(method); } -// std::string Cppyy::GetMethodMangledName(TCppMethod_t method) -// { -// if (method) -// return m2f(method)->GetMangledName(); -// return ""; -// } - Cppyy::TCppType_t Cppyy::GetMethodReturnType(TCppMethod_t method) { std::lock_guard Lock(InterOpMutex); @@ -1725,10 +1463,40 @@ std::string Cppyy::GetMethodSignature(TCppMethod_t method, bool show_formal_args return sig.str(); } -std::string Cppyy::GetMethodPrototype(TCppMethod_t method, bool show_formal_args) -{ - assert(0 && "Unused"); - return ""; // return Cpp::GetFunctionPrototype(method, show_formal_args); +Cppyy::TCppType_t Cppyy::GetFnTypeFromStdFn(TCppType_t fn_type) { + fn_type = Cpp::IsReferenceType(fn_type) ? Cpp::GetNonReferenceType(fn_type) : fn_type; + fn_type = Cpp::IsPointerType(fn_type) ? Cpp::GetPointeeType(fn_type) : fn_type; + TCppScope_t scope = Cpp::GetScopeFromType(fn_type); + std::vector args; + Cpp::GetClassTemplateArgs(scope, args); + assert(args.size() == 1); + if (args.size() == 1) + return args[0].m_Type; + return nullptr; +} + +void Cppyy::GetFnTypeSig(TCppType_t fn_type, std::vector& arg_types) { + fn_type = Cpp::IsReferenceType(fn_type) ? Cpp::GetNonReferenceType(fn_type) : fn_type; + fn_type = Cpp::IsPointerType(fn_type) ? Cpp::GetPointeeType(fn_type) : fn_type; + Cpp::GetFnTypeSignature(fn_type, arg_types); +} + +bool Cppyy::IsSameType(TCppType_t typ1, TCppType_t typ2) { + return Cpp::IsSameType(typ1, typ2); +} + +bool Cppyy::IsFunctionType(TCppType_t typ) { + typ = Cpp::IsReferenceType(typ) ? Cpp::GetNonReferenceType(typ) : typ; + typ = Cpp::IsPointerType(typ) ? Cpp::GetPointeeType(typ) : typ; + return Cpp::IsFunctionProtoType(typ); +} + +bool Cppyy::IsSimilarFnTypes(TCppType_t typ1, TCppType_t typ2) { + typ1 = Cpp::IsReferenceType(typ1) ? Cpp::GetNonReferenceType(typ1) : typ1; + typ2 = Cpp::IsReferenceType(typ2) ? Cpp::GetNonReferenceType(typ2) : typ2; + typ1 = Cpp::IsPointerType(typ1) ? Cpp::GetPointeeType(typ1) : typ1; + typ2 = Cpp::IsPointerType(typ2) ? Cpp::GetPointeeType(typ2) : typ2; + return Cpp::IsSameType(typ1, typ2); } std::string Cppyy::GetDoxygenComment(TCppScope_t scope, bool strip_markers) @@ -1765,7 +1533,7 @@ std::string Cppyy::GetTemplatedMethodName(TCppScope_t scope, TCppIndex_t imeth) std::vector mc; Cpp::GetFunctionTemplatedDecls(scope, mc); - if (imeth < mc.size()) return Cpp::GetName(mc[imeth]); + if (imeth < mc.size()) return Cpp::GetName(TCppScope_t(mc[imeth].data)); return ""; } @@ -1832,14 +1600,12 @@ Cppyy::TCppMethod_t Cppyy::GetMethodTemplate( if (!cppmeth && unresolved_candidate_methods.size() == 1 && !templ_params.empty()) { cppmeth = Cpp::InstantiateTemplate( - unresolved_candidate_methods[0], templ_params.data(), - templ_params.size(), /*instantiate_body=*/false); + TCppScope_t(unresolved_candidate_methods[0].data), templ_params.data(), + templ_params.size(), /*instantiate_body=*/false).data; } - return cppmeth; - + return TCppMethod_t(cppmeth.data); // if it fails, use Sema to propogate info about why it failed (DeductionInfo) - } static inline std::string type_remap(const std::string& n1, @@ -1863,7 +1629,7 @@ static inline std::string type_remap(const std::string& n1, void Cppyy::GetClassOperators(Cppyy::TCppScope_t klass, const std::string& opname, - std::vector& operators) { + std::vector& operators) { std::lock_guard Lock(InterOpMutex); std::string op = opname.substr(8); Cpp::GetOperator(klass, Cpp::GetOperatorFromSpelling(op), operators, @@ -1871,19 +1637,19 @@ void Cppyy::GetClassOperators(Cppyy::TCppScope_t klass, } Cppyy::TCppMethod_t Cppyy::GetGlobalOperator( - TCppType_t scope, const std::string& lc, const std::string& rc, const std::string& opname) + TCppScope_t scope, const std::string& lc, const std::string& rc, const std::string& opname) { std::string rc_type = type_remap(rc, lc); std::string lc_type = type_remap(lc, rc); - std::vector overloads; + std::vector overloads; Cpp::GetOperator(scope, Cpp::GetOperatorFromSpelling(opname), overloads, /*kind=*/Cpp::OperatorArity::kBoth); // Avoid pushing nullptr into arg_types which would crash // BestOverloadFunctionMatch when it dereferences each entry's QualType. auto resolve_arg_type = [](const std::string& name) -> Cppyy::TCppType_t { - if (auto s = Cppyy::GetScope(name, 0)) + if (auto s = Cppyy::GetScope(name)) if (auto t = Cppyy::GetTypeFromScope(s)) return Cppyy::GetReferencedType(t); return Cppyy::GetType(name, /*enable_slow_lookup=*/true); @@ -1891,13 +1657,13 @@ Cppyy::TCppMethod_t Cppyy::GetGlobalOperator( std::vector arg_types; if (auto l = resolve_arg_type(lc_type)) - arg_types.emplace_back(l); + arg_types.emplace_back(l.data); else return nullptr; if (!rc_type.empty()) { if (auto r = resolve_arg_type(rc_type)) - arg_types.emplace_back(r); + arg_types.emplace_back(r.data); else return nullptr; } @@ -1908,7 +1674,7 @@ Cppyy::TCppMethod_t Cppyy::GetGlobalOperator( return nullptr; } -// // method properties --------------------------------------------------------- +// method properties --------------------------------------------------------- bool Cppyy::IsDeletedMethod(TCppMethod_t method) { return Cpp::IsFunctionDeleted(method); @@ -1949,23 +1715,7 @@ bool Cppyy::IsExplicit(TCppMethod_t method) return Cpp::IsExplicit(method); } -// -// // 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? -// } - +// data member reflection information ---------------------------------------- void Cppyy::GetDatamembers(TCppScope_t scope, std::vector& datamembers) { std::lock_guard Lock(InterOpMutex); @@ -1999,10 +1749,10 @@ Cppyy::TCppScope_t Cppyy::WrapLambdaFromVariable(TCppScope_t var) { return var; } -Cppyy::TCppScope_t Cppyy::AdaptFunctionForLambdaReturn(TCppScope_t fn) { +Cppyy::TCppMethod_t Cppyy::AdaptFunctionForLambdaReturn(Cppyy::TCppMethod_t fn) { std::lock_guard Lock(InterOpMutex); - std::string fn_name = Cpp::GetQualifiedCompleteName(fn); + std::string fn_name = Cpp::GetQualifiedCompleteName(TCppScope_t(fn.data)); std::string signature = Cppyy::GetMethodSignature(fn, true); std::ostringstream call; @@ -2023,35 +1773,11 @@ Cppyy::TCppScope_t Cppyy::AdaptFunctionForLambdaReturn(TCppScope_t fn) { if (Cppyy::Compile(code.str().c_str())) { TCppScope_t res = Cpp::GetNamed( name, Cpp::GetScope("__cppyy_internal_wrap_g", /*parent=*/nullptr)); - if (res) return res; + if (res) return TCppMethod_t(res.data); } return fn; } -// 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(); -// } -// -// 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); -// } -// return count; -// } - Cppyy::TCppType_t Cppyy::GetDatamemberType(TCppScope_t var) { std::lock_guard Lock(InterOpMutex); @@ -2077,77 +1803,6 @@ intptr_t Cppyy::GetDatamemberOffset(TCppScope_t var, TCppScope_t klass) return Cpp::GetVariableOffset(Cpp::GetUnderlyingScope(var), klass); } -// static inline -// Cppyy::TCppIndex_t gb2idx(TGlobal* gb) -// { -// 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; -// } -// -// Cppyy::TCppIndex_t Cppyy::GetDatamemberIndexEnumerated(TCppScope_t scope, TCppIndex_t idata) -// { -// if (scope == GLOBAL_HANDLE) { -// TGlobal* gb = (TGlobal*)((THashList*)gROOT->GetListOfGlobals(false [> load <]))->At((int)idata); -// return gb2idx(gb); -// } -// -// return idata; -// } - // data member properties ---------------------------------------------------- bool Cppyy::IsPublicData(TCppScope_t datamem) { @@ -2174,10 +1829,10 @@ bool Cppyy::IsConstVar(TCppScope_t var) return Cpp::IsConstVariable(var); } -Cppyy::TCppScope_t Cppyy::ReduceReturnType(TCppScope_t fn, TCppType_t reduce) { +Cppyy::TCppMethod_t Cppyy::ReduceReturnType(TCppMethod_t fn, TCppType_t reduce) { std::lock_guard Lock(InterOpMutex); - std::string fn_name = Cpp::GetQualifiedCompleteName(fn); + std::string fn_name = Cpp::GetQualifiedCompleteName(TCppScope_t(fn.data)); std::string signature = Cppyy::GetMethodSignature(fn, true); std::string result_type = Cppyy::GetTypeAsString(reduce); @@ -2199,51 +1854,11 @@ Cppyy::TCppScope_t Cppyy::ReduceReturnType(TCppScope_t fn, TCppType_t reduce) { if (Cppyy::Compile(code.str().c_str())) { TCppScope_t res = Cpp::GetNamed( name, Cpp::GetScope("__cppyy_internal_wrap_g", /*parent=*/nullptr)); - if (res) return res; + if (res) return TCppMethod_t(res.data); } return fn; } -// bool Cppyy::IsEnumData(TCppScope_t scope, TCppIndex_t idata) -// { -// // 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. - -// if (scope == GLOBAL_HANDLE) { -// TGlobal* gbl = g_globalvars[idata]; - -// // 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); -// } - -// 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) -// 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()); -// } -// } -// } - -// // this default return only means that the data will be writable, not that it will -// // be unreadable or otherwise misrepresented -// return false; -// } - std::vector Cppyy::GetDimensions(TCppType_t type) { std::lock_guard Lock(InterOpMutex); @@ -2269,17 +1884,6 @@ Cppyy::TCppIndex_t Cppyy::GetEnumDataValue(TCppScope_t scope) return Cpp::GetEnumConstantValue(scope); } -// std::string Cppyy::GetEnumDataName(TCppEnum_t etype, TCppIndex_t idata) -// { -// return ((TEnumConstant*)((TEnum*)etype)->GetConstants()->At((int)idata))->GetName(); -// } -// -// long long Cppyy::GetEnumDataValue(TCppEnum_t etype, TCppIndex_t idata) -// { -// TEnumConstant* ecst = (TEnumConstant*)((TEnum*)etype)->GetConstants()->At((int)idata); -// return (long long)ecst->GetValue(); -// } - Cppyy::TCppScope_t Cppyy::InstantiateTemplate( TCppScope_t tmpl, Cpp::TemplateArgInfo* args, size_t args_size) { diff --git a/src/backend/cpp_cppyy.h b/src/backend/cpp_cppyy.h index 33c812c..8b2bbcf 100644 --- a/src/backend/cpp_cppyy.h +++ b/src/backend/cpp_cppyy.h @@ -1,6 +1,7 @@ #ifndef CPYCPPYY_CPPYY_H #define CPYCPPYY_CPPYY_H +#include #include // Standard @@ -11,6 +12,7 @@ #include #include +#include "precommondefs.h" #include "callcontext.h" // some more types; assumes Cppyy.h follows Python.h @@ -46,21 +48,21 @@ static inline size_t CALL_NARGS(size_t nargs) { } 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; + typedef Cpp::DeclRef TCppScope_t; + typedef Cpp::TypeRef TCppType_t; + typedef Cpp::ObjectRef TCppObject_t; + typedef Cpp::FuncRef TCppMethod_t; + typedef Cpp::InterpRef TInterp_t; + typedef size_t TCppIndex_t; + typedef void* TCppFuncAddr_t; -// // direct interpreter access ------------------------------------------------- +// direct interpreter access ------------------------------------------------- RPY_EXPORTED bool Compile(const std::string& code, bool silent = false); RPY_EXPORTED - std::string ToString(TCppType_t klass, TCppObject_t obj); + std::string ToString(TCppScope_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 @@ -98,7 +100,7 @@ namespace Cppyy { TCppType_t GetComplexType(const std::string &element_type); RPY_EXPORTED TCppScope_t GetScope(const std::string& scope_name, - TCppScope_t parent_scope = 0); + TCppScope_t parent_scope = TCppScope_t{}); RPY_EXPORTED TCppScope_t GetUnderlyingScope(TCppScope_t scope); RPY_EXPORTED @@ -107,7 +109,7 @@ namespace Cppyy { TCppScope_t GetTypeScope(TCppScope_t klass); RPY_EXPORTED TCppScope_t GetNamed(const std::string& scope_name, - TCppScope_t parent_scope = 0); + TCppScope_t parent_scope = TCppScope_t{}); RPY_EXPORTED TCppScope_t GetParentScope(TCppScope_t scope); RPY_EXPORTED @@ -122,8 +124,6 @@ namespace Cppyy { 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); @@ -134,10 +134,7 @@ namespace Cppyy { RPY_EXPORTED bool IsComplete(TCppScope_t type); -// RPY_EXPORTED -// inline TCppScope_t gGlobalScope = 0; // for fast access -// -// // memory management --------------------------------------------------------- +// memory management --------------------------------------------------------- RPY_EXPORTED TCppObject_t Allocate(TCppScope_t scope); RPY_EXPORTED @@ -183,7 +180,7 @@ namespace Cppyy { 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 @@ -193,7 +190,7 @@ namespace Cppyy { RPY_EXPORTED size_t GetFunctionArgTypeoffset(); -// // scope reflection information ---------------------------------------------- +// scope reflection information ---------------------------------------------- RPY_EXPORTED bool IsNamespace(TCppScope_t scope); RPY_EXPORTED @@ -213,7 +210,7 @@ namespace Cppyy { RPY_EXPORTED bool IsEnumType(TCppType_t type); RPY_EXPORTED - bool IsAggregate(TCppType_t type); + bool IsAggregate(TCppScope_t type); RPY_EXPORTED bool IsDefaultConstructable(TCppScope_t scope); RPY_EXPORTED @@ -222,19 +219,17 @@ namespace Cppyy { 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 ---------------------------------------------- - RPY_EXPORTED - std::string GetFinalName(TCppType_t type); + +// class reflection information ---------------------------------------------- RPY_EXPORTED - std::string GetScopedFinalName(TCppType_t type); + std::string GetFinalName(TCppScope_t type); RPY_EXPORTED - bool HasVirtualDestructor(TCppType_t type); + std::string GetScopedFinalName(TCppScope_t type); RPY_EXPORTED - bool HasComplexHierarchy(TCppType_t type) { assert(0 && "HasComplexHierarchy"); return false; } + bool HasVirtualDestructor(TCppScope_t type); RPY_EXPORTED TCppIndex_t GetNumBases(TCppScope_t klass); RPY_EXPORTED @@ -244,39 +239,26 @@ namespace Cppyy { RPY_EXPORTED TCppScope_t GetBaseScope(TCppScope_t klass, TCppIndex_t ibase); RPY_EXPORTED - bool IsSubclass(TCppType_t derived, TCppType_t base); + bool IsSubclass(TCppScope_t derived, TCppScope_t base); RPY_EXPORTED 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&) { assert(0 && "AddSmartPtrType"); return; } - - RPY_EXPORTED - 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 + bool GetSmartPtrInfo(const std::string&, TCppScope_t* raw, TCppMethod_t* deref); +// 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); + TCppScope_t derived, TCppScope_t base, TCppObject_t address, int direction, bool rerror = false); -// // method/function reflection information ------------------------------------ +// method/function reflection information ------------------------------------ RPY_EXPORTED void GetClassMethods(TCppScope_t scope, std::vector &methods); RPY_EXPORTED - std::vector GetMethodsFromName(TCppScope_t scope, + std::vector GetMethodsFromName(TCppScope_t scope, const std::string& name); - RPY_EXPORTED - TCppMethod_t GetMethod(TCppScope_t scope, TCppIndex_t imeth) { return 0; } - - RPY_EXPORTED - std::string GetMethodName(TCppMethod_t); + std::string GetName(TCppScope_t); RPY_EXPORTED - std::string GetMethodFullName(TCppMethod_t); - // GetMethodMangledName is unused. - RPY_EXPORTED - std::string GetMethodMangledName(TCppMethod_t) { assert(0 && "GetMethodMangledName"); return ""; } + std::string GetFullName(TCppScope_t); RPY_EXPORTED TCppType_t GetMethodReturnType(TCppMethod_t); RPY_EXPORTED @@ -299,14 +281,21 @@ namespace Cppyy { std::string GetMethodArgDefault(TCppMethod_t, TCppIndex_t iarg); RPY_EXPORTED std::string GetMethodSignature(TCppMethod_t, bool show_formal_args, TCppIndex_t max_args = (TCppIndex_t)-1); - // GetMethodPrototype is unused. RPY_EXPORTED - std::string GetMethodPrototype(TCppMethod_t, bool show_formal_args); + bool IsFunctionType(TCppType_t typ); + RPY_EXPORTED + TCppType_t GetFnTypeFromStdFn(TCppType_t fn_type); + RPY_EXPORTED + void GetFnTypeSig(TCppType_t fn_type, std::vector& arg_types); + RPY_EXPORTED + bool IsSameType(TCppType_t typ1, TCppType_t typ2); + RPY_EXPORTED + bool IsSimilarFnTypes(TCppType_t typ1, TCppType_t typ2); RPY_EXPORTED std::string GetDoxygenComment(TCppScope_t scope, bool strip_markers = true); RPY_EXPORTED bool IsConstMethod(TCppMethod_t); -// // Templated method/function reflection information ------------------------------------ +// Templated method/function reflection information ------------------------------------ RPY_EXPORTED void GetTemplatedMethods(TCppScope_t scope, std::vector &methods); RPY_EXPORTED @@ -324,10 +313,10 @@ namespace Cppyy { TCppScope_t scope, const std::string& name, const std::string& proto); RPY_EXPORTED void GetClassOperators(Cppyy::TCppScope_t klass, const std::string& opname, - std::vector& operators); + std::vector& operators); RPY_EXPORTED TCppMethod_t GetGlobalOperator( - TCppType_t scope, const std::string& lc, const std::string& rc, const std::string& op); + TCppScope_t scope, const std::string& lc, const std::string& rc, const std::string& op); // method properties --------------------------------------------------------- RPY_EXPORTED @@ -347,21 +336,15 @@ namespace Cppyy { RPY_EXPORTED bool IsExplicit(TCppMethod_t method); -// // data member reflection information ---------------------------------------- - // GetNumDatamembers is unused. - // RPY_EXPORTED - // TCppIndex_t GetNumDatamembers(TCppScope_t scope, bool accept_namespace = false) { return 0; } +// data member reflection information ---------------------------------------- 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 TCppScope_t WrapLambdaFromVariable(TCppScope_t var); RPY_EXPORTED - TCppScope_t AdaptFunctionForLambdaReturn(TCppScope_t fn); + TCppMethod_t AdaptFunctionForLambdaReturn(TCppMethod_t fn); RPY_EXPORTED TCppType_t GetDatamemberType(TCppScope_t data); RPY_EXPORTED @@ -385,22 +368,13 @@ namespace Cppyy { RPY_EXPORTED bool IsConstVar(TCppScope_t var); RPY_EXPORTED - TCppScope_t ReduceReturnType(TCppScope_t fn, TCppType_t reduce); - // IsEnumData is unused. - // RPY_EXPORTED - // bool IsEnumData(TCppScope_t scope, TCppIndex_t idata); + TCppMethod_t ReduceReturnType(TCppMethod_t fn, TCppType_t reduce); RPY_EXPORTED std::vector GetDimensions(TCppType_t type); -// // enum properties ----------------------------------------------------------- - // GetEnum is unused. - // RPY_EXPORTED - // TCppEnum_t GetEnum(TCppScope_t scope, const std::string& enum_name) { return 0; } +// enum properties ----------------------------------------------------------- RPY_EXPORTED std::vector GetEnumConstants(TCppScope_t scope); - // GetEnumDataName is unused. - // RPY_EXPORTED - // std::string GetEnumDataName(TCppEnum_t, TCppIndex_t idata) { return ""; } RPY_EXPORTED TCppType_t GetEnumConstantType(TCppScope_t scope); RPY_EXPORTED diff --git a/src/backend/cppinterop_dispatch.cxx b/src/backend/cppinterop_dispatch.cxx index 076f47e..5d70020 100644 --- a/src/backend/cppinterop_dispatch.cxx +++ b/src/backend/cppinterop_dispatch.cxx @@ -1,6 +1,6 @@ #include #if __has_include("CppInterOp/CppInterOpAPI.inc") -using namespace CppImpl; +using namespace Cpp; #define CPPINTEROP_API_FUNC(DN, CN, Ret, DeclArgs, CallArgs, RawTypes) \ Ret (*CppInternal::DispatchRaw::DN) RawTypes = nullptr; #include "CppInterOp/CppInterOpAPI.inc" diff --git a/test/cpp11features.cxx b/test/cpp11features.cxx index 7319875..edbe069 100644 --- a/test/cpp11features.cxx +++ b/test/cpp11features.cxx @@ -40,6 +40,18 @@ TestSmartPtr create_TestSmartPtr_by_value() { return TestSmartPtr{}; } +std::shared_ptr create_shared_ptr_to_derived() { + return std::shared_ptr(new PubDerivedTestSmartPtr); +} + +std::unique_ptr create_unique_ptr_to_derived() { + return std::unique_ptr(new PubDerivedTestSmartPtr); +} + +std::unique_ptr create_unique_ptr_to_offset_derived() { + return std::unique_ptr(new MultiDerivedTestSmartPtr); +} + // for move ctors etc. int TestMoving1::s_move_counter = 0; diff --git a/test/cpp11features.h b/test/cpp11features.h index 86c26be..17d7a78 100644 --- a/test/cpp11features.h +++ b/test/cpp11features.h @@ -3,6 +3,7 @@ #include #include +#include #include @@ -39,6 +40,48 @@ int move_unique_ptr_derived(std::unique_ptr&& p); TestSmartPtr create_TestSmartPtr_by_value(); +// for auto-downcast of objects returned through a smart pointer +class PubDerivedTestSmartPtr : public TestSmartPtr { +public: + int only_in_derived() { return 27; } +}; + +// second base so that the cross-cast to the most derived type needs a +// non-zero pointer adjustment, which the smart pointer's dereferencer can +// not apply consistently (so no down-cast should happen in that case) +class TestSmartPtrIface { +public: + virtual ~TestSmartPtrIface() {} + long m_pad = 0; + int only_in_iface() { return 37; } +}; + +class MultiDerivedTestSmartPtr : public PubDerivedTestSmartPtr, public TestSmartPtrIface { +}; + +std::shared_ptr create_shared_ptr_to_derived(); +std::unique_ptr create_unique_ptr_to_derived(); +std::unique_ptr create_unique_ptr_to_offset_derived(); + +// sinks expecting a smart pointer to the *derived* type; a base-class smart +// pointer (even when its object was auto-down-cast) must not be accepted here +int pass_unique_ptr_to_derived(std::unique_ptr p) { + return p->only_in_derived(); +} + +int pass_shared_ptr_to_derived(std::shared_ptr p) { + return p->only_in_derived(); +} + +// Overloaded function to check if automatic downcasting is consistently +// applied for regular proxy objects and smart pointer proxies. +std::string pass_ptr_overloaded(TestSmartPtr *) { return "TestSmartPtr"; } +std::string pass_ptr_overloaded(PubDerivedTestSmartPtr *) { return "PubDerivedTestSmartPtr"; } +std::string pass_ref_overloaded(TestSmartPtr &) { return "TestSmartPtr"; } +std::string pass_ref_overloaded(PubDerivedTestSmartPtr &) { return "PubDerivedTestSmartPtr"; } +std::string pass_val_overloaded(TestSmartPtr) { return "TestSmartPtr"; } +std::string pass_val_overloaded(PubDerivedTestSmartPtr) { return "PubDerivedTestSmartPtr"; } + //=========================================================================== class TestMoving1 { // for move ctors etc. diff --git a/test/datatypes.cxx b/test/datatypes.cxx index 571877f..7bb4016 100644 --- a/test/datatypes.cxx +++ b/test/datatypes.cxx @@ -105,6 +105,7 @@ CppyyTestData::~CppyyTestData() void CppyyTestData::destroy_arrays() { if (m_owns_arrays == true) { delete[] m_bool_array2; + delete[] m_schar_array2; delete[] m_uchar_array2; delete[] m_byte_array2; delete[] m_int8_array2; @@ -1009,4 +1010,4 @@ MULTIDIM_ARRAYS_NEW2D(double, double) namespace Int8_Uint8_Arrays { int8_t test[6] = {-0x12, -0x34, -0x56, -0x78}; uint8_t utest[6] = { 0x12, 0x34, 0x56, 0x78}; -} \ No newline at end of file +} diff --git a/test/test_cpp11features.py b/test/test_cpp11features.py index 78d401c..51abe67 100644 --- a/test/test_cpp11features.py +++ b/test/test_cpp11features.py @@ -566,3 +566,50 @@ def pyfunc() -> std.shared_ptr[ns.Dummy]: return ns.dummy_create() assert ns.call_creator(pyfunc) + + def test21_smart_ptr_downcast(self): + """Object returned through a smart pointer is auto-downcast""" + + import cppyy + + gbl = cppyy.gbl + + # unique_ptr holding a Derived comes back as Derived, with the + # derived-only method callable, just like a raw pointer return + for cf in [gbl.create_unique_ptr_to_derived, gbl.create_shared_ptr_to_derived]: + obj = cf() + assert type(obj) == gbl.PubDerivedTestSmartPtr + assert obj.only_in_derived() == 27 + assert obj.__smartptr__() # smart-pointer semantics preserved + + # an object that really is of the declared type stays that type + obj = gbl.create_unique_ptr_instance() + assert type(obj) == gbl.TestSmartPtr + + # the most derived type sits at a non-zero offset from the declared + # interface, which the dereferencer can not apply: stay the declared + # type and keep behaving correctly + obj = gbl.create_unique_ptr_to_offset_derived() + assert type(obj) == gbl.TestSmartPtrIface + assert obj.only_in_iface() == 37 + + # the auto-down-cast must not enable C++-invalid conversions: the proxy + # still embeds a smart pointer to the *base* type, which does not convert + # to a smart pointer to the derived type (no implicit down-conversion of + # smart pointers in C++), so passing it to such a sink must be rejected + raises(TypeError, gbl.pass_unique_ptr_to_derived, gbl.create_unique_ptr_to_derived()) + raises(TypeError, gbl.pass_shared_ptr_to_derived, gbl.create_shared_ptr_to_derived()) + + # passing it where the matching base smart pointer is expected still works + assert gbl.pass_shared_ptr(gbl.create_shared_ptr_to_derived()) == 17 + + # calling function with overloads for both the base class and the + # derived class should resolve to the downcasted type overload, + # no matter if the Python proxy is a regular proxy or wraps a smart pointer + # (should hold for pointer, reference, and value types) + assert gbl.pass_ptr_overloaded(gbl.PubDerivedTestSmartPtr()) == "PubDerivedTestSmartPtr" + assert gbl.pass_ptr_overloaded(gbl.create_unique_ptr_to_derived()) == "PubDerivedTestSmartPtr" + assert gbl.pass_ref_overloaded(gbl.PubDerivedTestSmartPtr()) == "PubDerivedTestSmartPtr" + assert gbl.pass_ref_overloaded(gbl.create_unique_ptr_to_derived()) == "PubDerivedTestSmartPtr" + assert gbl.pass_val_overloaded(gbl.PubDerivedTestSmartPtr()) == "PubDerivedTestSmartPtr" + assert gbl.pass_val_overloaded(gbl.create_unique_ptr_to_derived()) == "PubDerivedTestSmartPtr" diff --git a/test/test_cpp23features.py b/test/test_cpp23features.py new file mode 100644 index 0000000..9e93dc5 --- /dev/null +++ b/test/test_cpp23features.py @@ -0,0 +1,459 @@ +import os, sys, subprocess +from pytest import mark + + +# C++23 isn't the stack's default and the interpreter is a process-wide +# singleton pinned at the first `import cppyy`. So TestCPP23Driver re-runs the +# whole class once in a child process that selects C++23 before import +# (EXTRA_CLING_ARGS for cling, CPPINTEROP_EXTRA_INTERPRETER_ARGS for clang-repl); +# all tests then share that one interpreter. To iterate locally, run the tests +# directly in "child mode": +# +# CPPYY_TEST_CPP23_CHILD=1 EXTRA_CLING_ARGS=-std=c++23 \ +# CPPINTEROP_EXTRA_INTERPRETER_ARGS=-std=c++23 \ +# pytest -v test_cpp23features.py + +_CPP23_CHILD = "CPPYY_TEST_CPP23_CHILD" +_IN_CHILD = bool(os.environ.get(_CPP23_CHILD)) + + +@mark.skipif(not _IN_CHILD, + reason="C++23 tests run in the child interpreter launched by " + "TestCPP23Driver") +class TestCPP23FEATURES: + """C++23 features driven via cppyy.cppdef through the JIT (clang-repl/cling). + + The class owns one C++23 interpreter (booted in setup_class); tests share + it via ``self.cppyy``. + """ + + @classmethod + def setup_class(cls): + # In the child process C++23 is already selected, so this one-time boot + # is the C++23 interpreter every test shares. + import cppyy + cls.cppyy = cppyy + + def test01_deducing_this_basic(self): + """Explicit object parameter on a member function (P0847R7)""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct Widget { + int value = 42; + int get(this Widget& self) { return self.value; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.Widget() + assert w.value == 42 + assert w.get() == 42 + + def test02_deducing_this_const(self): + """const explicit object parameter (this const Widget& self)""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct CWidget { + int value = 7; + int read(this const CWidget& self) { return self.value; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.CWidget() + assert w.read() == 7 + + def test03_deducing_this_by_value(self): + """by-value explicit object parameter (this Widget self)""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct VWidget { + int value = 5; + int snapshot(this VWidget self) { return self.value; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.VWidget() + w.value = 11 + assert w.snapshot() == 11 + + def test04_deducing_this_with_extra_args(self): + """explicit object parameter alongside regular arguments""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct AWidget { + int base = 100; + int add(this AWidget& self, int x, int y) { return self.base + x + y; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.AWidget() + assert w.add(20, 3) == 123 + + def test05_deducing_this_rvalue_ref(self): + """rvalue-ref-qualified explicit object parameter (this Widget&&)""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct RWidget { + int value = 13; + int consume(this RWidget&& self) { return self.value; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.RWidget() + assert w.consume() == 13 + + def test05b_traditional_rvalue_ref_qualifier(self): + """traditional rvalue-ref-qualified member (int f() &&), no deducing this""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct QWidget { + int value = 17; + int consume() && { return value; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.QWidget() + assert w.consume() == 17 + + def test06_deducing_this_chaining(self): + """builder-style chaining: explicit object parameter returns self""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct BWidget { + int value = 0; + BWidget& set(this BWidget& self, int v) { self.value = v; return self; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.BWidget() + r = w.set(5).set(8) + assert r.value == 8 + assert w.value == 8 # same object returned by reference + + def test07_deducing_this_default_arg(self): + """explicit object parameter with a defaulted regular argument""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct DWidget { + int base = 3; + int scale(this DWidget& self, int factor = 4) { return self.base * factor; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.DWidget() + assert w.scale() == 12 # default factor=4 + assert w.scale(10) == 30 + + def test08_deducing_this_call_operator(self): + """call operator with an explicit object parameter""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct Adder { + int base = 100; + int operator()(this Adder& self, int x) { return self.base + x; } + }; + } + """) + + a = cppyy.gbl.Cpp23DeducingThis.Adder() + assert a(23) == 123 + + def test09_deducing_this_mixed_overload(self): + """overload set mixing an explicit-object and a normal member function""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct MWidget { + int value = 50; + int get(this MWidget& self) { return self.value; } + int get(int extra) { return value + extra; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.MWidget() + assert w.get() == 50 # explicit-object overload + assert w.get(7) == 57 # normal overload + + def test10_deducing_this_templated(self): + """templated explicit object parameter (the CRTP-replacement idiom)""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct TWidget { + int value = 9; + template + int via(this Self&& self) { return self.value; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.TWidget() + assert w.via() == 9 + + def test11_deducing_this_templated_with_args(self): + """templated explicit object parameter alongside a regular argument""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct TAWidget { + int base = 40; + template + int plus(this Self&& self, int x) { return self.base + x; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.TAWidget() + assert w.plus(2) == 42 + + def test12_deducing_this_templated_returns_self_type(self): + """deduced Self drives the return: returns the deduced object's field""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct TRWidget { + int value = 99; + template + auto identity(this Self&& self) { return self.value; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.TRWidget() + assert w.identity() == 99 + + def test13_deducing_this_abbreviated_auto(self): + """abbreviated `this auto&&` form (the canonical CRTP-replacement idiom)""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct AAWidget { + int value = 23; + int get(this auto&& self) { return self.value; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.AAWidget() + assert w.get() == 23 + + def test14_deducing_this_by_value_copy_semantics(self): + """by-value `this W self` operates on an independent copy""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct CopyWidget { + int value = 1; + int bump(this CopyWidget self) { self.value += 100; return self.value; } + }; + } + """) + + w = cppyy.gbl.Cpp23DeducingThis.CopyWidget() + assert w.bump() == 101 # the copy is mutated + assert w.value == 1 # ... the original is untouched + + def test15_deducing_this_inheritance(self): + """base-class explicit object method invoked on a derived object""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct Base { + int value = 8; + int get(this Base& self) { return self.value; } + }; + struct Derived : Base { }; + } + """) + + d = cppyy.gbl.Cpp23DeducingThis.Derived() + assert d.get() == 8 + + # ------------------------------------------------------------------ # + # Use-cases from the Microsoft C++ blog post "C++23's Deducing this": + # https://devblogs.microsoft.com/cppblog/cpp23-deducing-this/ + # ------------------------------------------------------------------ # + + def test16_blog_deduplication(self): + """Blog use-case 1: code de-duplication of cv/ref accessors.""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + #include + namespace Cpp23DeducingThis { + struct Optional { + int m_value = 5; + // one accessor, all value categories (returns a reference) + template auto&& value(this Self&& self) { + return std::forward(self).m_value; + } + // by-value flavor, for a clean read from Python + template auto read(this Self&& self) { + return std::forward(self).m_value; + } + }; + } + """) + + o = cppyy.gbl.Cpp23DeducingThis.Optional() + assert o.read() == 5 # forwarding read + o.value()[0] = 17 # write through the forwarded reference + assert o.read() == 17 # ... mutation is visible on the object + + def test17_blog_crtp_postfix_increment(self): + """Blog use-case 2: CRTP postfix increment without templating the base. + + The using-declaration re-exposes the inherited postfix (standard name + hiding); driven from C++ as Python has no postfix-increment syntax. + """ + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct add_postfix_increment { + template + auto operator++(this Self&& self, int) { auto tmp = self; ++self; return tmp; } + }; + struct some_type : add_postfix_increment { + using add_postfix_increment::operator++; // un-hide inherited postfix + int v = 0; + some_type& operator++() { ++v; return *this; } + }; + // old.v should be the pre-increment value, c.v the post. + int drive_postfix() { some_type c; auto old = c++; return old.v * 100 + c.v; } + } + """) + + assert cppyy.gbl.Cpp23DeducingThis.drive_postfix() == 1 # old.v=0, c.v=1 + + def test18_blog_recursive_lambda(self): + """Blog use-case 4: recursive lambda via the explicit object parameter.""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + int fib(int n) { + auto f = [](this auto const& self, int n) -> int { + return n < 2 ? n : self(n - 1) + self(n - 2); + }; + return f(n); + } + } + """) + + assert cppyy.gbl.Cpp23DeducingThis.fib(10) == 55 + + def test19_blog_lambda_forwarding(self): + """Blog use-case 3: closure with an explicit object parameter. + + The blog uses std::forward_like (not in the host libstdc++); this + exercises the explicit-object closure mechanism itself. + """ + cppyy = self.cppyy + + assert cppyy.cppdef(""" + #include + namespace Cpp23DeducingThis { + struct Scheduler { int submitted = 0; int submit(int m) { submitted = m; return m; } }; + int run_callback() { + Scheduler scheduler; + int message = 42; + auto callback = [message, &scheduler](this auto&& self) -> int { + return scheduler.submit(message); + }; + return callback(); + } + } + """) + + assert cppyy.gbl.Cpp23DeducingThis.run_callback() == 42 + + def test20_blog_pass_by_value(self): + """Blog use-case 5: pass the object by value for better codegen on small types.""" + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + struct just_a_little_guy { + int how_smol = 21; + int uwu(this just_a_little_guy self) { return self.how_smol * 2; } + }; + } + """) + + assert cppyy.gbl.Cpp23DeducingThis.just_a_little_guy().uwu() == 42 + + def test21_blog_sfinae_friendly_transform(self): + """Blog use-case 6: SFINAE-friendly optional::transform. + + Driven from C++: cppyy can't resolve a two-template-parameter + explicit-object method against a Python callable. + """ + cppyy = self.cppyy + + assert cppyy.cppdef(""" + namespace Cpp23DeducingThis { + template + struct Optional6 { + T m_value; + template + auto transform(this Self&& self, F&& f) { return f(self.m_value); } + }; + int triple(int x) { return x * 3; } + int drive_transform() { Optional6 o{14}; return o.transform(triple); } + } + """) + + assert cppyy.gbl.Cpp23DeducingThis.drive_transform() == 42 + + +@mark.skipif(_IN_CHILD, reason="launcher runs only in the parent process") +class TestCPP23Driver: + """Re-run TestCPP23FEATURES once in a child interpreter pinned to C++23.""" + + def test_run_under_cpp23(self): + env = dict(os.environ) + env[_CPP23_CHILD] = "1" + env["EXTRA_CLING_ARGS"] = "-std=c++23" # cling + env["CPPINTEROP_EXTRA_INTERPRETER_ARGS"] = "-std=c++23" # clang-repl + proc = subprocess.run( + [sys.executable, "-m", "pytest", "-q", "-p", "no:cacheprovider", + os.path.basename(__file__)], + cwd=os.path.dirname(os.path.abspath(__file__)), + env=env, capture_output=True, text=True) + assert proc.returncode == 0, ( + "C++23 child run failed (rc=%d)\n--- stdout ---\n%s\n--- stderr ---\n%s" + % (proc.returncode, proc.stdout, proc.stderr)) diff --git a/test/test_datatypes.py b/test/test_datatypes.py index fb58408..fc6d450 100644 --- a/test/test_datatypes.py +++ b/test/test_datatypes.py @@ -2442,3 +2442,35 @@ def test54_optional_use(self): assert ns54.teu8.value_or(ns54.Teu8.TWO) == ns54.Teu8.TWO ns54.teu8 = ns54.Teu8.TWO assert ns54.teu8 == ns54.Teu8.TWO + + def test55_qt_cache_alias_collision(self): + """`unsigned char` and `uint8_t` share a canonical type but cppyy + wires them to different converters (str vs int). Locks in the + QT-keyed cache's alias-collision skip in Converters.cxx.""" + + import cppyy + + cppyy.cppdef(""" + #include + namespace ns55 { + void take_uchar(unsigned char) {} + void take_uint8(uint8_t) {} + void take_schar(signed char) {} + void take_int8(int8_t) {} + } + """) + ns = cppyy.gbl.ns55 + + # `unsigned char`: UCharConverter -- accepts 1-char str. + ns.take_uchar('e') + ns.take_uchar(101) # int also accepted as a char value + raises(TypeError, ns.take_uchar, 1.5) + + # `uint8_t`: UInt8Converter -- accepts int. + ns.take_uint8(101) + raises(TypeError, ns.take_uint8, 'e') + + # Symmetric for the signed pair. + ns.take_schar('e') + ns.take_int8(101) + raises(TypeError, ns.take_int8, 'e') diff --git a/test/test_fragile.py b/test/test_fragile.py index e469ca7..5c39c51 100644 --- a/test/test_fragile.py +++ b/test/test_fragile.py @@ -241,12 +241,15 @@ def test11_dir(self): #else #define CPPYY_IMPORT extern #endif + + namespace Cpp { + struct DeclRef; + } // namespace Cpp namespace Cppyy { + typedef Cpp::DeclRef TCppScope_t; - typedef void* TCppScope_t; - - CPPYY_IMPORT TCppScope_t GetScope(const std::string& scope_name, TCppScope_t parent_scope = nullptr); + CPPYY_IMPORT TCppScope_t GetScope(const std::string& scope_name, TCppScope_t parent_scope = TCppScope_t()); CPPYY_IMPORT void GetAllCppNames(TCppScope_t scope, std::set& cppnames); }""") diff --git a/test/test_regression.py b/test/test_regression.py index 1488165..942807c 100644 --- a/test/test_regression.py +++ b/test/test_regression.py @@ -1443,3 +1443,140 @@ def test48_templated_using_with_const(self): """) assert gbl.NN["std::vector::value_type, 5"]().F is True + + def test50_using_decl_base_this_offset(self): + """A using-declaration-imported method from a non-zero-offset base must + adjust the `this` pointer to that base's subobject. + + When a derived class pulls a base method into its own overload set with + `using Base::method;` (typically to merge it with a same-named local + overload), the method is still declared in the base. If that base is not + the first one, its subobject sits at a non-zero offset in the derived + object. The `this` offset used to be computed against the class the + method was bound on (the derived class) rather than its declaring base, + yielding a zero offset; the call then wrote through an unadjusted + pointer, corrupting memory and crashing on destruction. + """ + + import cppyy + + cppyy.cppdef(r""" + namespace UsingDeclThisOffset { + + // Fat first base so that SecondBase lands at a non-zero offset in Derived. + struct FirstBase { + long long a, b, c, d, e, f, g, h; + FirstBase() : a(11), b(22), c(33), d(44), e(55), f(66), g(77), h(88) {} + long long get_a() const { return a; } + long long get_h() const { return h; } + }; + + struct SecondBase { + int value; + SecondBase() : value(-1) {} + void set_value(int v) { value = v; } // 1-arg setter in a non-zero-offset base + int get_value() const { return value; } + }; + + struct Derived : public FirstBase, public SecondBase { + int extra; + Derived() : extra(0) {} + using SecondBase::set_value; // import the 1-arg overload + void set_value(int v, int w) { value = v + w; extra = w; } // local 2-arg overload + }; + + std::ptrdiff_t secondbase_offset() { + Derived* d = new Derived(); + std::ptrdiff_t off = (char*)static_cast(d) - (char*)d; + delete d; + return off; + } + + } """) + + ns = cppyy.gbl.UsingDeclThisOffset + + # the bug only manifests when SecondBase is at a non-zero offset + assert ns.secondbase_offset() != 0 + + d = ns.Derived() + + # the 1-arg call resolves to the using-imported SecondBase::set_value(int) + d.set_value(42) + + # the value lands in the correct SecondBase subobject ... + assert d.get_value() == 42 + # ... and the FirstBase subobject (at offset 0) is left untouched; with a + # wrong (zero) `this` offset the write would have clobbered FirstBase::a. + assert d.get_a() == 11 + assert d.get_h() == 88 + + # the local 2-arg overload still works and likewise leaves FirstBase intact + d.set_value(10, 5) + assert d.get_value() == 15 + assert d.extra == 5 + assert d.get_a() == 11 + + # destruction must not crash (heap integrity preserved) + del d + + def test51_nontype_enum_template_arg(self): + """Regression test for a class template with a non-type enum parameter + + + clang prints such an argument as a C-style cast, e.g. + ``NonTypeEnumTmpl::Impl``. Resolving + that name (as happens when a base pointer is auto-downcast to its + actual derived type) used to either fail to instantiate the template + ("non-type template parameter must be an expression") or leave the + interpreter in an error state that broke the next, unrelated JIT call + wrapper ("failed to resolve function"). + """ + + import cppyy + + cppyy.cppdef(r""" + namespace NonTypeEnumTmpl { + enum class EOp { Add = 0, Sub = 1, Mul = 2 }; + + struct Base { + virtual ~Base() {} + virtual int code() const = 0; + }; + + template + struct Impl : public Base { + int code() const override { return (int)Op; } + }; + + // Factory returning a *raw* base pointer whose dynamic type carries + // a non-type enum template argument. Auto-downcasting it back to + // Python makes cppyy resolve the actual type by name, i.e. the + // cast-form "Impl". + Base* get(int which) { + static Impl a; + static Impl s; + static Impl m; + if (which == 0) return &a; + if (which == 1) return &s; + return &m; + } + + // Called after the downcast to detect interpreter poisoning: its + // call wrapper is JIT-compiled only on first use. + int probe(int x) { return x + 1; } + } + """) + + ns = cppyy.gbl.NonTypeEnumTmpl + + # resolving the derived template type during the auto-downcast must not + # crash and must yield the actual (derived) class... + op = ns.get(0) + assert op.code() == 0 + assert 'Impl