From a594bd1a73767a6f6b1b14947212908f623b26ea Mon Sep 17 00:00:00 2001 From: Vipul Cariappa Date: Tue, 14 Oct 2025 13:48:28 +0200 Subject: [PATCH] support nested array of objects --- src/Converters.cxx | 244 +++++++++++++++++++++------------------ src/TupleOfInstances.cxx | 7 +- 2 files changed, 136 insertions(+), 115 deletions(-) diff --git a/src/Converters.cxx b/src/Converters.cxx index fef3014d..a32b4238 100644 --- a/src/Converters.cxx +++ b/src/Converters.cxx @@ -1622,119 +1622,131 @@ bool CPyCppyy::VoidArrayConverter::ToMemory(PyObject* value, void* address, PyOb //---------------------------------------------------------------------------- -#define CPPYY_IMPL_ARRAY_CONVERTER(name, ctype, type, code, suffix) \ -CPyCppyy::name##ArrayConverter::name##ArrayConverter(cdims_t dims) : \ - fShape(dims) { \ - fIsFixed = dims ? fShape[0] != UNKNOWN_SIZE : false; \ -} \ - \ -bool CPyCppyy::name##ArrayConverter::SetArg( \ - PyObject* pyobject, Parameter& para, CallContext* ctxt) \ -{ \ - /* filter ctypes first b/c their buffer conversion will be wrong */ \ - bool convOk = false; \ - \ - /* 2-dim case: ptr-ptr types */ \ - if (fShape.ndim() == 2) { \ - if (Py_TYPE(pyobject) == GetCTypesPtrType(ct_##ctype)) { \ - para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;\ - para.fTypeCode = 'p'; \ - convOk = true; \ - } else if (Py_TYPE(pyobject) == GetCTypesType(ct_c_void_p)) { \ - /* special case: pass address of c_void_p buffer to return the address */\ - para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;\ - para.fTypeCode = 'p'; \ - convOk = true; \ - } else if (LowLevelView_Check(pyobject) && \ - ((LowLevelView*)pyobject)->fBufInfo.ndim == 2 && \ - strchr(((LowLevelView*)pyobject)->fBufInfo.format, code)) { \ - para.fValue.fVoidp = ((LowLevelView*)pyobject)->get_buf(); \ - para.fTypeCode = 'p'; \ - convOk = true; \ - } \ - } \ - \ - /* 1-dim (accept pointer), or unknown (accept pointer as cast) */ \ - if (!convOk) { \ - PyTypeObject* ctypes_type = GetCTypesType(ct_##ctype); \ - if (Py_TYPE(pyobject) == ctypes_type) { \ - para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;\ - para.fTypeCode = 'p'; \ - convOk = true; \ - } else if (Py_TYPE(pyobject) == GetCTypesPtrType(ct_##ctype)) { \ - para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)pyobject)->b_ptr;\ - para.fTypeCode = 'V'; \ - convOk = true; \ - } else if (IsPyCArgObject(pyobject)) { \ - CPyCppyy_tagPyCArgObject* carg = (CPyCppyy_tagPyCArgObject*)pyobject;\ - if (carg->obj && Py_TYPE(carg->obj) == ctypes_type) { \ - para.fValue.fVoidp = (void*)((CPyCppyy_tagCDataObject*)carg->obj)->b_ptr;\ - para.fTypeCode = 'p'; \ - convOk = true; \ - } \ - } \ - } \ - \ - /* cast pointer type */ \ - if (!convOk) { \ - bool ismulti = fShape.ndim() > 1; \ - convOk = CArraySetArg(pyobject, para, code, ismulti ? sizeof(void*) : sizeof(type), true);\ - } \ - \ - /* memory management and offsetting */ \ - if (convOk) SetLifeLine(ctxt->fPyContext, pyobject, (intptr_t)this); \ - \ - return convOk; \ -} \ - \ -PyObject* CPyCppyy::name##ArrayConverter::FromMemory(void* address) \ -{ \ - if (!fIsFixed) \ - return CreateLowLevelView##suffix((type**)address, fShape); \ - return CreateLowLevelView##suffix(*(type**)address, fShape); \ -} \ - \ -bool CPyCppyy::name##ArrayConverter::ToMemory( \ - PyObject* value, void* address, PyObject* ctxt) \ -{ \ - if (fShape.ndim() <= 1 || fIsFixed) { \ - void* buf = nullptr; \ - Py_ssize_t buflen = Utility::GetBuffer(value, code, sizeof(type), buf);\ - if (buflen == 0) \ - return false; \ - \ - Py_ssize_t oldsz = 1; \ - for (Py_ssize_t idim = 0; idim < fShape.ndim(); ++idim) { \ - if (fShape[idim] == UNKNOWN_SIZE) { \ - oldsz = -1; \ - break; \ - } \ - oldsz *= fShape[idim]; \ - } \ - if (fShape.ndim() != UNKNOWN_SIZE && 0 < oldsz && oldsz < buflen) { \ - PyErr_SetString(PyExc_ValueError, "buffer too large for value"); \ - return false; \ - } \ - \ - if (fIsFixed) \ - memcpy(*(type**)address, buf, (0 < buflen ? buflen : 1)*sizeof(type));\ - else { \ - *(type**)address = (type*)buf; \ - fShape.ndim(1); \ - fShape[0] = buflen; \ - SetLifeLine(ctxt, value, (intptr_t)address); \ - } \ - \ - } else { /* multi-dim, non-flat array; assume structure matches */ \ - void* buf = nullptr; /* TODO: GetBuffer() assumes flat? */ \ - Py_ssize_t buflen = Utility::GetBuffer(value, code, sizeof(void*), buf);\ - if (buflen == 0) return false; \ - *(type**)address = (type*)buf; \ - SetLifeLine(ctxt, value, (intptr_t)address); \ - } \ - return true; \ -} - +#define CPPYY_IMPL_ARRAY_CONVERTER(name, ctype, type, code, suffix) \ + CPyCppyy::name##ArrayConverter::name##ArrayConverter(cdims_t dims) \ + : fShape(dims) { \ + fIsFixed = dims ? fShape[0] != UNKNOWN_SIZE : false; \ + } \ + \ + bool CPyCppyy::name##ArrayConverter::SetArg( \ + PyObject *pyobject, Parameter ¶, CallContext *ctxt) { \ + /* filter ctypes first b/c their buffer conversion will be wrong */ \ + bool convOk = false; \ + \ + /* 2-dim case: ptr-ptr types */ \ + if (!convOk && fShape.ndim() == 2) { \ + if (Py_TYPE(pyobject) == GetCTypesPtrType(ct_##ctype)) { \ + para.fValue.fVoidp = \ + (void *)((CPyCppyy_tagCDataObject *)pyobject)->b_ptr; \ + para.fTypeCode = 'p'; \ + convOk = true; \ + } else if (Py_TYPE(pyobject) == GetCTypesType(ct_c_void_p)) { \ + /* special case: pass address of c_void_p buffer to return the address \ + */ \ + para.fValue.fVoidp = \ + (void *)((CPyCppyy_tagCDataObject *)pyobject)->b_ptr; \ + para.fTypeCode = 'p'; \ + convOk = true; \ + } else if (LowLevelView_Check(pyobject) && \ + ((LowLevelView *)pyobject)->fBufInfo.ndim == 2 && \ + strchr(((LowLevelView *)pyobject)->fBufInfo.format, code)) { \ + para.fValue.fVoidp = ((LowLevelView *)pyobject)->get_buf(); \ + para.fTypeCode = 'p'; \ + convOk = true; \ + } \ + } \ + \ + /* 1-dim (accept pointer), or unknown (accept pointer as cast) */ \ + if (!convOk) { \ + PyTypeObject *ctypes_type = GetCTypesType(ct_##ctype); \ + if (Py_TYPE(pyobject) == ctypes_type) { \ + para.fValue.fVoidp = \ + (void *)((CPyCppyy_tagCDataObject *)pyobject)->b_ptr; \ + para.fTypeCode = 'p'; \ + convOk = true; \ + } else if (Py_TYPE(pyobject) == GetCTypesPtrType(ct_##ctype)) { \ + para.fValue.fVoidp = \ + (void *)((CPyCppyy_tagCDataObject *)pyobject)->b_ptr; \ + para.fTypeCode = 'V'; \ + convOk = true; \ + } else if (IsPyCArgObject(pyobject)) { \ + CPyCppyy_tagPyCArgObject *carg = (CPyCppyy_tagPyCArgObject *)pyobject; \ + if (carg->obj && Py_TYPE(carg->obj) == ctypes_type) { \ + para.fValue.fVoidp = \ + (void *)((CPyCppyy_tagCDataObject *)carg->obj)->b_ptr; \ + para.fTypeCode = 'p'; \ + convOk = true; \ + } \ + } else if (LowLevelView_Check(pyobject) && \ + strchr(((LowLevelView *)pyobject)->fBufInfo.format, code)) { \ + para.fValue.fVoidp = ((LowLevelView *)pyobject)->get_buf(); \ + para.fTypeCode = 'p'; \ + convOk = true; \ + } \ + } \ + \ + /* cast pointer type */ \ + if (!convOk) { \ + bool ismulti = fShape.ndim() > 1; \ + convOk = CArraySetArg(pyobject, para, code, \ + ismulti ? sizeof(void *) : sizeof(type), true); \ + } \ + \ + /* memory management and offsetting */ \ + if (convOk) \ + SetLifeLine(ctxt->fPyContext, pyobject, (intptr_t)this); \ + \ + return convOk; \ + } \ + \ + PyObject *CPyCppyy::name##ArrayConverter::FromMemory(void *address) { \ + if (!fIsFixed) \ + return CreateLowLevelView##suffix((type **)address, fShape); \ + return CreateLowLevelView##suffix(*(type **)address, fShape); \ + } \ + \ + bool CPyCppyy::name##ArrayConverter::ToMemory( \ + PyObject *value, void *address, PyObject *ctxt) { \ + if (fShape.ndim() <= 1 || fIsFixed) { \ + void *buf = nullptr; \ + Py_ssize_t buflen = Utility::GetBuffer(value, code, sizeof(type), buf); \ + if (buflen == 0) \ + return false; \ + \ + Py_ssize_t oldsz = 1; \ + for (Py_ssize_t idim = 0; idim < fShape.ndim(); ++idim) { \ + if (fShape[idim] == UNKNOWN_SIZE) { \ + oldsz = -1; \ + break; \ + } \ + oldsz *= fShape[idim]; \ + } \ + if (fShape.ndim() != UNKNOWN_SIZE && 0 < oldsz && oldsz < buflen) { \ + PyErr_SetString(PyExc_ValueError, "buffer too large for value"); \ + return false; \ + } \ + \ + if (fIsFixed) \ + memcpy(*(type **)address, buf, \ + (0 < buflen ? buflen : 1) * sizeof(type)); \ + else { \ + *(type **)address = (type *)buf; \ + fShape.ndim(1); \ + fShape[0] = buflen; \ + SetLifeLine(ctxt, value, (intptr_t)address); \ + } \ + \ + } else { /* multi-dim, non-flat array; assume structure matches */ \ + void *buf = nullptr; /* TODO: GetBuffer() assumes flat? */ \ + Py_ssize_t buflen = \ + Utility::GetBuffer(value, code, sizeof(void *), buf); \ + if (buflen == 0) \ + return false; \ + *(type **)address = (type *)buf; \ + SetLifeLine(ctxt, value, (intptr_t)address); \ + } \ + return true; \ + } //---------------------------------------------------------------------------- CPPYY_IMPL_ARRAY_CONVERTER(Bool, c_bool, bool, '?', ) @@ -2415,6 +2427,12 @@ bool CPyCppyy::InstanceArrayConverter::SetArg( PyObject* pyobject, Parameter& para, CallContext* /* txt */) { // convert to C++ instance**, set arg for call + while (PyTuple_Check(pyobject) && !TupleOfInstances_CheckExact(pyobject)) { + if (PyTuple_Size(pyobject) > 0) + pyobject = PyTuple_GetItem(pyobject, 0); + else + return false; + } if (!TupleOfInstances_CheckExact(pyobject)) return false; // no guarantee that the tuple is okay diff --git a/src/TupleOfInstances.cxx b/src/TupleOfInstances.cxx index 6ef09a24..622bc5d1 100644 --- a/src/TupleOfInstances.cxx +++ b/src/TupleOfInstances.cxx @@ -146,8 +146,11 @@ PyObject* TupleOfInstances_New( return (PyObject*)ia; } else if (1 < dims.ndim()) { // not the innermost dimension, descend one level - size_t block_size = 0; - for (Py_ssize_t i = 1; i < dims.ndim(); ++i) block_size += (size_t)dims[i]; + size_t block_size = 1; + for (Py_ssize_t i = 1; i < dims.ndim(); ++i) { + if (dims[i] != 0) + block_size *= (size_t)dims[i]; + } block_size *= Cppyy::SizeOf(klass); Py_ssize_t nelems = dims[0];