From d48f13298c1974addad9da8ed9c2cdbc0a504a85 Mon Sep 17 00:00:00 2001
From: Stefan Wunsch <stefan.wunsch@cern.ch>
Date: Wed, 13 Mar 2019 14:27:16 +0100
Subject: [PATCH] [PyROOT exp] Port fix for adoption of empty std::vector

---
 .../PyROOT/python/ROOT/pythonization/_rvec.py |  5 +++-
 .../PyROOT/src/PyzPythonHelpers.cxx           |  4 +--
 .../PyROOT/test/array_interface.py            | 29 +++++++++++++++++++
 3 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_rvec.py b/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_rvec.py
index a875f6904d3..612c33370bb 100644
--- a/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_rvec.py
+++ b/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_rvec.py
@@ -33,7 +33,10 @@ def get_array_interface(self):
             dtype_size = GetSizeOfType(dtype)
             endianess = GetEndianess()
             size = self.size()
-            pointer = GetVectorDataPointer(self, cppname)
+            if self.empty(): # Numpy sees a null pointer as error even though the data is never accessed.
+                pointer = 1
+            else:
+                pointer = GetVectorDataPointer(self, cppname)
             return {
                 "shape": (size, ),
                 "typestr": "{}{}{}".format(endianess, dtype_numpy, dtype_size),
diff --git a/bindings/pyroot_experimental/PyROOT/src/PyzPythonHelpers.cxx b/bindings/pyroot_experimental/PyROOT/src/PyzPythonHelpers.cxx
index bd5bbcef65f..91fa1ad43f1 100644
--- a/bindings/pyroot_experimental/PyROOT/src/PyzPythonHelpers.cxx
+++ b/bindings/pyroot_experimental/PyROOT/src/PyzPythonHelpers.cxx
@@ -72,14 +72,14 @@ PyObject *PyROOT::GetVectorDataPointer(PyObject * /*self*/, PyObject *args)
    std::string cppname = CPyCppyy_PyUnicode_AsString(pycppname);
 
    // Call interpreter to get pointer to data (using `data` method)
-   long pointer;
+   unsigned long long pointer;
    std::stringstream code;
    code << "*((long*)" << &pointer << ") = reinterpret_cast<long>(reinterpret_cast<" << cppname << "*>(" << cppobj
         << ")->data())";
    gInterpreter->Calc(code.str().c_str());
 
    // Return pointer as integer
-   PyObject *pypointer = PyInt_FromLong(pointer);
+   PyObject *pypointer = PyLong_FromUnsignedLongLong(pointer);
    return pypointer;
 }
 
diff --git a/bindings/pyroot_experimental/PyROOT/test/array_interface.py b/bindings/pyroot_experimental/PyROOT/test/array_interface.py
index 544682795fe..d64ced8fa89 100644
--- a/bindings/pyroot_experimental/PyROOT/test/array_interface.py
+++ b/bindings/pyroot_experimental/PyROOT/test/array_interface.py
@@ -4,6 +4,11 @@ import numpy as np
 
 
 class ArrayInterface(unittest.TestCase):
+    """
+    Test memory adoption of std::vector and ROOT::RVec with the numpy
+    array interface.
+    """
+
     # Helpers
     dtypes = [
         "int", "unsigned int", "long", "unsigned long", "float", "double"
@@ -32,6 +37,9 @@ class ArrayInterface(unittest.TestCase):
 
     # Tests
     def test_RVec(self):
+        """
+        Test correct adoption of different datatypes for std::vector
+        """
         for dtype in self.dtypes:
             root_obj = ROOT.ROOT.VecOps.RVec(dtype)(2)
             np_obj = np.asarray(root_obj)
@@ -39,12 +47,33 @@ class ArrayInterface(unittest.TestCase):
             self.check_shape((2, ), np_obj)
 
     def test_STLVector(self):
+        """
+        Test correct adoption of different datatypes for ROOT::RVec
+        """
         for dtype in self.dtypes:
             root_obj = ROOT.std.vector(dtype)(2)
             np_obj = np.asarray(root_obj)
             self.check_memory_adoption(root_obj, np_obj)
             self.check_shape((2, ), np_obj)
 
+    def test_STLVector_empty(self):
+        """
+        Test adoption of empty std::vector
+        """
+        root_obj = ROOT.std.vector("float")()
+        np_obj = np.asarray(root_obj)
+        self.assertEqual(np_obj.shape, (0,))
+        self.assertEqual(np_obj.__array_interface__["data"][0], 1)
+
+    def test_RVec_empty(self):
+        """
+        Test adoption of empty ROOT::RVec
+        """
+        root_obj = ROOT.ROOT.VecOps.RVec("float")()
+        np_obj = np.asarray(root_obj)
+        self.assertEqual(np_obj.shape, (0,))
+        self.assertEqual(np_obj.__array_interface__["data"][0], 1)
+
 
 if __name__ == '__main__':
     unittest.main()
-- 
GitLab