CPyCppyy icon indicating copy to clipboard operation
CPyCppyy copied to clipboard

ScopeProxy doesn't update when c++ symbol updates

Open wrongbad opened this issue 1 year ago • 3 comments

Here's a complete reproducible example:

>>> import cppyy
>>> cppyy.include("iostream")
True
>>> cppyy.cppexec("int x = 1; std::cout << x << std::endl;")
1
True
>>> cppyy.gbl.x
1
>>> cppyy.cppexec("int x = 2; std::cout << x << std::endl;")
2
True
>>> cppyy.gbl.x
1
>>> del cppyy.gbl.x
>>> cppyy.cppexec("int x = 3; std::cout << x << std::endl;")
3
True
>>> cppyy.gbl.x
3

I would expect the 2nd eval of cppyy.gbl.x to return 2 but it seems to have cached the value 1 until I explicitly delete the cached value.

wrongbad avatar Jan 13 '24 03:01 wrongbad

Yes, on the Cling side, the redeclaration simply masks the first declaration. Ie., there are 2 global variables named x in the system; it's not an update of the original variable. Since lookups are expensive and there is no callback from Cling to indicate that a variable has been redeclared, there's no easy way to propagate such an update other than forcing a fresh lookup by using an explicit del.

Note that the same is true in Cling:

>>> import cppyy
>>> cppyy.cppexec("int x = 1;")
True
>>> cppyy.cppdef("int f() { return x; }")
True
>>> cppyy.gbl.f()
1
>>> cppyy.cppexec("int x = 2;")
True
>>> cppyy.gbl.f()
1
>>> cppyy.cppdef("int g() { return x; }")
True
>>> cppyy.gbl.g()
2
>>> 

Again, this is because a redeclaration isn't a replacement.

wlav avatar Jan 16 '24 19:01 wlav

I see. Thanks for the explanation.

And to be clear, you mean symbol lookups are slow, but looking up new values at the same address is fine to do each time right?

wrongbad avatar Jan 16 '24 20:01 wrongbad

Yes. Lookups are slow b/c on the Python side, all that is known is that name x, not what it is and thus where to find it. I.e., there's a whole range of lookups happening (is this a class? a function? an enum? a ...). Furthermore, the following is also legal in Cling:

>>> import cppyy
>>> cppyy.cppexec("int x = 1;")
True
>>> cppyy.gbl.x
1
>>> del cppyy.gbl.x
>>> cppyy.cppexec('class x {};')
True
>>> cppyy.gbl.x
<class cppyy.gbl.x at 0x149704dc0>
>>> 

Thus storing "x is a global variable of type int", although sufficient for the example above, is also not a general solution. I.e., an actual complete solution would require a complete lookup on every use, which will give a couple orders of magnitude performance reduction in regular use. (Note that erasing a class doesn't work the same as erasing an int global variable, so the opposite case doesn't actually work.)

wlav avatar Jan 16 '24 21:01 wlav