"int pass-by-ref not implemented in p3"

In Python 3, ints are longs and we no longer have PyIntObject. Hence:

https://github.com/root-mirror/root/blob/master/bindings/pyroot/src/Converters.cxx#L268

So code like this just doesn’t work with PyROOT in Python 3 because int pass-by-ref isn’t implemented:

>>> import ROOT
>>> h = ROOT.TH1D("asd", "asd", 10, 0, 1)
>>> x, y, z = ROOT.Long(0), ROOT.Long(0), ROOT.Long(0)
>>> h.GetBinXYZ(3, x, y, z)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NotImplementedError: void TH1::GetBinXYZ(int binglobal, int& binx, int& biny, int& binz) =>
    could not convert argument 2 (int pass-by-ref not implemented in p3)

Are there plans to implement this using PyLongObject?

Hi,

can’t use PyLongObject as it has no simple underlying C representation like PyIntObject has in p2 (which is just a C long value).

My long-standing idea is to give Cling a temporary and then copy this into the output. I.e. return a tuple with all byref-values returned there. That is generic enough to solve most cases. E.g. right now, you can’t pass an unsigned int by-ref. (Most, not all, as the C+±side could take a pointer to the value pass by-reference.)

An alternative is to use the ctypes types, which would cover all cases, but that is clunky on the python-side and ctypes does not publicly expose its internal structs.

Opinion on either?

Cheers,
Wim

For copying a temporary to the output, does this would mean that the PyROOT interface would be different than the regular ROOT interface?

E.g. something like:

void do_something(double x, double & y)

would have to be called in PyROOT with just one argument, and the return value given a name?

While I would support ROOT itself overhauling classes to return std::tuples instead of using “output arguments” to functions, I don’t know if I like the idea of PyROOT establishing different function signatures than ROOT. Would the docs be updated to make it obvious to users what is going on?

Jean-François

Hi Wim,

I’ll give it some thought. At the moment I think I’m leaning toward your first idea of giving Cling temporaries and returning a tuple, although this creates a conflict where a non-void function accepts values by reference.

ctypes might not be so bad… and would avoid the extra overhead of creating/copying temporaries.

Noel

Jean-François,

[quote=“jfcaron”] void do_something(double x, double & y)
would have to be called in PyROOT with just one argument, and the return value given a name?[/quote]That would look like this:result = do_something(x, y)with ‘y’ being any old float. This as opposed to:yref = ROOT.Double(y) do_something(x, yref) y = float(yref)as is currently the case, or would be with a ctypes equivalent (actually, the ctypes is worse as its underlying types can’t participate as normal: you can use the ROOT.Double yref instead of y after the call, it’d just be slower).

Noel, I’m not seeing the conflict? True, one could now do (per above):do_something(x, 3.14) which would be illegal in C++, but it’d still be perfectly safe.

As for ctypes, no: you create temporary python objects whereas the C++ ones are already created no matter what (to unbox the python object), so you don’t gain there. With the tuple return, there’s the creation/destruction of the tuple, so it’s probably a wash, but definitely nothing in favor of ctypes.

Thanks,
Wim

By conflict, I mean for functions like this:

int do_something(double x, double & y)

you would need to return the underlying int return value somewhere (just include it at the front of the returned tuple?).

Re ctypes, I agree. I’m in favour of returning the tuple.

Noel,

that’d be:result, y = do_something(x, y)My preference would not be to add the by-ref results at the front, b/c by adding at the back, the original return result stays in a fixed position.

Note that technically, it would even be possible to do both: with c_types arguments, return those, otherwise use the tuple: in the rewritten code in master, the calls carry a TCallContext object around, which stays alive until the call is finished. Hence state can now be shared easily between converters (which determine what should be returned) and executors (that do the returning). This TCallContext was created for by-ref returns: in PyPy, I had to allocate a second “argument stack” to store pointers to temporaries on the first, so that the pointers could be passed as reference arguments. That’s very clunky, and a sharable context object is more convenient.

I’m sitting on a half-finished large overhaul that requires me to redo everything for by-ref anyway (the change is to use a lower-level interface of Cling, i.e. the wrapper functions directly, for a sweet speedup, but at the cost of having to be way more specific with all types passed).

Cheers,
Wim

Any resolution for this issue?
Suggested workaround?

Hey, I got here because I was looking for a way to do pass-by-ref in pyroot with python3. I think it’s worth to post the solution I found on the website of the University of Zurich using arrays.

from array import array
x  = array('d',[0])
y = array('d',[0])
tgraph.GetPoint(1,x,y)