@@ -263,6 +263,11 @@ namespace CPyCppyy {
263263 static std::map<std::string, std::vector<PyObject*>> pyzMap;
264264 return pyzMap;
265265 }
266+
267+ // Forward-declared from CPyCppyy/API.h (which can't be #include'd here
268+ // because it would conflict with the internal Converters.h).
269+ PyObject* CreatePyValueFromMemory (
270+ const std::string& typeName, void * address, dim_t ndim = 0 , const dim_t * dims = nullptr );
266271}
267272
268273
@@ -816,6 +821,54 @@ static PyObject* BindObject(PyObject*, PyObject* args, PyObject* kwds)
816821 return BindCppObjectNoCast (addr, cast_type);
817822}
818823
824+ // ----------------------------------------------------------------------------
825+ static PyObject* CreateValueFromMemoryPy (PyObject*, PyObject* args)
826+ {
827+ // Python entry point for CPyCppyy::CreatePyValueFromMemory.
828+ //
829+ // Arguments:
830+ // type_name (str): C++ type name to convert from, e.g. "double", "int",
831+ // "double[]", "float*", "MyClass*", etc.
832+ // address (int): memory address to read from. For scalar (non-array)
833+ // types this is the address of the value. For array
834+ // types this is the address of the data array itself
835+ // (i.e. the value of the data pointer).
836+ // dims (sequence, optional): sequence of integers giving the shape of
837+ // the array. If provided, the value is treated as an
838+ // array (a LowLevelView is returned). If omitted or
839+ // None, the value is treated as a scalar.
840+ const char * typeName = nullptr ;
841+ PyObject* addressArg = nullptr ;
842+ PyObject* dimsArg = Py_None;
843+ if (!PyArg_ParseTuple (args, " sO|O:_create_value_from_memory" , &typeName, &addressArg, &dimsArg))
844+ return nullptr ;
845+
846+ void * address = PyLong_AsVoidPtr (addressArg);
847+ if (PyErr_Occurred ())
848+ return nullptr ;
849+
850+ std::vector<dim_t > dimsVec;
851+ if (dimsArg && dimsArg != Py_None) {
852+ if (!PySequence_Check (dimsArg)) {
853+ PyErr_SetString (PyExc_TypeError, " _create_value_from_memory: dims must be a sequence" );
854+ return nullptr ;
855+ }
856+ Py_ssize_t ndim = PySequence_Length (dimsArg);
857+ dimsVec.reserve (ndim);
858+ for (Py_ssize_t i = 0 ; i < ndim; ++i) {
859+ PyObject* item = PySequence_GetItem (dimsArg, i);
860+ if (!item) return nullptr ;
861+ dim_t d = (dim_t )PyLong_AsLongLong (item);
862+ Py_DECREF (item);
863+ if (PyErr_Occurred ()) return nullptr ;
864+ dimsVec.push_back (d);
865+ }
866+ }
867+
868+ return CPyCppyy::CreatePyValueFromMemory (
869+ typeName, address, (dim_t )dimsVec.size (), dimsVec.data ());
870+ }
871+
819872// ----------------------------------------------------------------------------
820873static PyObject* Move (PyObject*, PyObject* pyobject)
821874{
@@ -996,6 +1049,8 @@ static PyMethodDef gCPyCppyyMethods[] = {
9961049 METH_O , (char *)" Represent an array of objects as raw memory." },
9971050 {(char *)" bind_object" , (PyCFunction)BindObject,
9981051 METH_VARARGS | METH_KEYWORDS , (char *) " Create an object of given type, from given address." },
1052+ {(char *)" _create_value_from_memory" , (PyCFunction)CreateValueFromMemoryPy,
1053+ METH_VARARGS , (char *) " Build a Python value from a memory address using a Converter." },
9991054 {(char *) " move" , (PyCFunction)Move,
10001055 METH_O , (char *)" Cast the C++ object to become movable." },
10011056 {(char *) " add_pythonization" , (PyCFunction)AddPythonization,
0 commit comments