Pyroot, copy constructors, assignment

Dear Wim, dear all,

I’m thinking about how to expose an equivalent of the following bit of C++ to perl in SOOT, my perl-ROOT wrapper, and feel I’m missing something obvious. I bet that PyROOT (being much more mature than SOOT) has solved this elegantly and would like to know how. I hope this oddball question is welcome.

*gRandom = *otherTRandomInstance;

The use case being that I would like to invoke the copy constructor for typeof(gRandom) and update the gRandom container.

Does PyROOT support an operation like this? If so, could you point me at the code that implements it? Possibly, you’ve found a nice and general way to have that work as a side effect of having a proper design :slight_smile:

Best regards,
Steffen

Steffen,

it’s not straightforward in python, as there is a difference between:global_ptr = other_global_ptrwhich can’t be hooked, and this:module.global_ptr = other_global_ptrwhich can, by changing the module’s setattr function (and a few other thingies, among others that global ptr objects are exposed as proxies with a setter/getter callbacks).

Thus, ROOT.gRandom = TRandom() will do the expected thing, but this:from ROOT import gRandom gRandom = TRandom()will only modify the local ‘gRandom’ and the change will not be seen on the C++ side.

Note that this is a straight pointer setting: no copy constructor is called (the snippet you posted doesn’t call the copy constructor either in C++, but the assignment operator, which in PyROOT can be called, but that has to be done explicitly with the assign function through which it is exposed).

With all that being somewhat rather python-specific, what you really need is to be able to distinguish between global pointers and “ordinary” objects. Basically, whenever you get a global pointer b/c you found them in gROOT’s list of globals or in the CINT global scope (namespaces in PyROOT are classes, thus no such tricks are needed), then keep track of that fact (e.g. by having a different type of proxy; PyROOT simply uses a flag). With that, any module- or class-level assignment can behave differently based on that type.

If that is too involved or too invasive into existing code, then for a start, you could also turn the assignment into a call passed to gROOT.ProcessLine(). PyROOT does that to be able to create new global C++ variables from python through what looks to the user as a clean interface.

Cheers,
Wim

Hi Wim,

thanks for your very helpful reply. I’ll have to mull a bit over the best approach in my case for a little bit. Maybe I’ll use this as an opportunity to learn a bit more about Python’s guts and re-read some of your PyROOT cocde. I hear Python internals are a lot less nasty than Perl’s.

[quote=“wlav”]Steffen,

it’s not straightforward in python, as there is a difference between:global_ptr = other_global_ptrwhich can’t be hooked, and this:module.global_ptr = other_global_ptrwhich can, by changing the module’s setattr function (and a few other thingies, among others that global ptr objects are exposed as proxies with a setter/getter callbacks).
[/quote]

This sounds remotely similar to the problems in Perl. Here, $foo = $bar can’t be overloaded. Alas, neither can $Module::foo = $bar. It’d have to be some sort of method call or some other operation than assignment. What I settled for (maybe this was a bad choice) was to use overloading of function dereferencing as follows: $gRandom->( TRandom->new ) (ie. using the variable as a function reference). Maybe I should just have used $gRandom->assign(…), but that’s all just cosmetics.

facepalm My C++ is fading fast after not having used it much in a year. Regardless of that, I wonder whether I wasn’t having an XY problem. Let me assert: Since gRandom is a global of type TRandom*, if we want to update all places that refer to gRandom (C++ side), we should update it using *gRandom = *other instead of just changing the pointer, right?

I thought about usingProcessLIne(), but… but… it feels so wrong. That being said, I’m treating ROOT globals as slightly special already by having a flag set on them that prevents any Perl references to it from trying to ever free them. Since the annotation struct (yes, this Perl-interfacing stuff is C, not C++) already has a bool at the end, I am quite certain I can add quite a few more flags at zero cost thanks to alignment. Also, in the face of guessing which method to call, any other cost is negligible. That reminds me that I was going to auto-generate efficient method stubs for all non-ambiguous cases. On my puny laptop, the speed-up could be from sluggish O(50k) calls/s to speedy O(1M) calls/s. Alas, spare time is at a premium.

Thanks again!

Cheers,
Steffen

Steffen,

That is one possible choice, but not one that I made. Issue is that when the gRandom pointed to object is set, then we have two objects of the same (the one assigned into *gRandom, and the original “other”). That can be confusing in certain circumstances, and what is worse, some ROOT objects set globals themselves in their constructors, making it even more confusing.

What happens in PyROOT in some more detail, is that if an object is a global pointer, the actual value carried is not stuffed in a void* directly, but rather a (void*)&global_ptr. The flag is then set to know whether on retrieval/access of the object, a straight void* should be returned, or a (void*). Thus, python always sees a void*, with PyROOT mediating.

For setting then, what is set is *(&global_ptr), i.e. global_ptr. That is a straight pointer copy (i.e. change of the value of the global_ptr variable) and thus visible to any client that uses global_ptr. Memory management then gets a bit more icky (a python-side created object now becomes C++ owned, and the previous object may disappear into oblivion rather then be destructed), but it uses a single object only. Meaning, if a user does:

r = TRandom ROOT.gRandom = r r.SetSeed(some_seed)
both r and gRandom will be seeded. (If you assign the object, gRandom would not be seeded.)

Globals are wrong and ugly anyway, so as long there is some way of dealing with them, I doubt that it matters that it is slow or “wrong” in any practical setting.

Is there a JIT-project for perl? With PyPy, I find great improvements far beyond what is possible with CPython because the whole call layer (i.e. all of PyROOT) can be removed, once JIT-ed.

Yah, I feel that pain too, especially now that US ATLAS management has told me to no longer develop PyROOT in company time.

Best wishes for 2012, and good luck.

Cheers,
Wim