Say I have this:
class A { int k; void B() { write("foo %d\n", k); } void C() { k = 3; write("bar\n"); } }
int main() { A a = A(); a->C(); // Displays: bar a->B(); // Displays: foo 3 // At this point I want to replace the function B // in the running/compiled instance of A in a. // I.e. I want to call compile() or similar // on the following code: // void B() { write("FOO %d\n", k); } // such that I can subsequently run: a->B(); // Should display: FOO 3 return 0; }
Any way this can be accomplished? An alternate way would be to compile the whole class of A again, but then run method B() in it with a custom this argument pointing to the old instance in a. Is that possible? I know javascript can do this, but it seems like Pike does not allow/support it.
On Mon, May 3, 2021 at 2:04 AM Stephen R. van den Berg srb@cuci.nl wrote:
Say I have this:
class A { int k; void B() { write("foo %d\n", k); } void C() { k = 3; write("bar\n"); } }
int main() { A a = A(); a->C(); // Displays: bar a->B(); // Displays: foo 3 // At this point I want to replace the function B // in the running/compiled instance of A in a. // I.e. I want to call compile() or similar // on the following code: // void B() { write("FOO %d\n", k); } // such that I can subsequently run: a->B(); // Should display: FOO 3 return 0; }
Any way this can be accomplished? An alternate way would be to compile the whole class of A again, but then run method B() in it with a custom this argument pointing to the old instance in a. Is that possible? I know javascript can do this, but it seems like Pike does not allow/support it.
JavaScript has some utterly bizarre rules about the 'this' reference that I wouldn't want to see any other language replicate. In Pike, a function remembers the context it was created in, so what you're trying to do won't work with injection. But perhaps subclassing can do what you want - instead of compiling the entire class again, create a subclass that replaces that one function.
ChrisA
Chris Angelico wrote:
trying to do won't work with injection. But perhaps subclassing can do what you want - instead of compiling the entire class again, create a subclass that replaces that one function.
Yes, well, that won't cut it, I'm afraid, because I specifically want that function to be able to pretend that he is living inside the already live/running older object of the old class (needs access to those variables/members).
I'm trying to see how close I can get to rapid development where you actually replace methods inside live classes during runtime.
On a related question then, if I do this:
class D { class E { void F() { } } function G() { E e = E(); return e->F; } }
int main() { D d = D(); function ref = d->G(); d = 0; gc(); // At this point, does object d still // have references? // Or is it gone because from F // we do not refrence D? return 0; }
The closest I could see is to not really have the functions directly, I guess, but an mapping of functions, and overload `() so that it pulls the function from the mapping, then you could easily replace the function by replacing the reference in the mapping. The replaced function would be locator collected,or you could implement it as a list of hooks. Could be transparent to calling code and be effectively the same thing, even if implemented differently. Unless I am misunderstanding how `() works.
Sent from Yahoo Mail on Android
On Sun, May 2, 2021 at 12:32 PM, Stephen R. van den Bergsrb@cuci.nl wrote: Chris Angelico wrote:
trying to do won't work with injection. But perhaps subclassing can do what you want - instead of compiling the entire class again, create a subclass that replaces that one function.
Yes, well, that won't cut it, I'm afraid, because I specifically want that function to be able to pretend that he is living inside the already live/running older object of the old class (needs access to those variables/members).
I'm trying to see how close I can get to rapid development where you actually replace methods inside live classes during runtime.
On a related question then, if I do this:
class D { class E { void F() { } } function G() { E e = E(); return e->F; } }
int main() { D d = D(); function ref = d->G(); d = 0; gc(); // At this point, does object d still // have references? // Or is it gone because from F // we do not refrence D? return 0; }
Lance Dillon wrote:
The closest I could see is to not really have the functions directly, I guess, but an mapping of functions, and overload `() so that it pulls the function from the mapping, then you could easily replace the function by replacing the reference in the mapping. The replaced function would be locator collected,or you could implement it as a list of hooks. Could be transparent to calling code and be effectively the same thing, even if implemented differently. Unless I am misunderstanding how `() works.
I think that method breaks at the point where you need to manipulate "this" to point to the main class.
Ah well, I am restructuring the code so that the live class in question is being referenced through a variable. It seems that is the only practical solution.
On Mon, May 3, 2021 at 3:35 AM Stephen R. van den Berg srb@cuci.nl wrote:
Lance Dillon wrote:
The closest I could see is to not really have the functions directly, I guess, but an mapping of functions, and overload `() so that it pulls the function from the mapping, then you could easily replace the function by replacing the reference in the mapping. The replaced function would be locator collected,or you could implement it as a list of hooks. Could be transparent to calling code and be effectively the same thing, even if implemented differently. Unless I am misunderstanding how `() works.
I think that method breaks at the point where you need to manipulate "this" to point to the main class.
Ah well, I am restructuring the code so that the live class in question is being referenced through a variable. It seems that is the only practical solution.
My usual practice is to completely recompile the class, and have a single mapping for all "carry-over" state, something like this:
class A { mapping s = ([]); void B() {...} void C() {...} }
And then to replace anything, I'd create a new instance of the newly-compiled class, replace its empty state mapping with the same one as the old object, and start using that. It's simple, reliable, and safe (if any old code is still referenced, it'll use the same state mapping), and doesn't require weird shenanigans.
It might be kinda nice to have some sort of compiler support for changing the 'this' context of a function, but it's really not something that I'd use very often.
ChrisA
Chris Angelico wrote:
My usual practice is to completely recompile the class, and have a single mapping for all "carry-over" state, something like this:
And then to replace anything, I'd create a new instance of the newly-compiled class, replace its empty state mapping with the same one as the old object, and start using that. It's simple, reliable,
Yes, if the class in completely in the source you control, that is fine. But in this case the class comes from a sort-of library (from which I inherit), and there is more state information than just the config, like:
- Caches. - Database connection pools. - Active sockets. - Active sessions. - Various call_out timers still pending. - Statistics.
On Mon, May 3, 2021 at 4:25 AM Stephen R. van den Berg srb@cuci.nl wrote:
Chris Angelico wrote:
My usual practice is to completely recompile the class, and have a single mapping for all "carry-over" state, something like this:
And then to replace anything, I'd create a new instance of the newly-compiled class, replace its empty state mapping with the same one as the old object, and start using that. It's simple, reliable,
Yes, if the class in completely in the source you control, that is fine. But in this case the class comes from a sort-of library (from which I inherit), and there is more state information than just the config, like:
- Caches.
- Database connection pools.
- Active sockets.
- Active sessions.
- Various call_out timers still pending.
- Statistics.
Ah, that does make it more complicated. Particularly with call_outs.
I don't think there's an easy way to do this.
ChrisA
pike-devel@lists.lysator.liu.se