ScopeProxy doesn't update when c++ symbol updates
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.
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.
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?
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.)