www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - change object class

reply Vitaliy Fadeev <vital.fadeev gmail.com> writes:
Hi!
I want to change a method ```Draw``` on a custom object when the 
```MouseIn``` event occurs.
This is known as "Change State" of the object: ```Init``` -> 
```Hovered```.

I want to change the state of an object by changing its class, 
like this:

```d

this.__vptr = typeid(CLS).vtbl.ptr;

```

I have read the ABI and am confident in replacing ```__vptr``` as 
long as the classes contain the same fields and the same 
interfaces.

Example:
```d
// O
//   to!state
// State_Init    : O
//   Draw
// State_Hovered : O
//   Draw
//
// o.to!State_Hovered
// o.to!State_Init

class O
{
   void to(CLS)()
   {
     // if (same fields && same interfaces && same instance size)
     this.__vptr =
       cast(immutable(void*)*)typeid(CLS).vtbl.ptr;
   }
}

State_Init : O
   void Draw() { /* ... */ }

State_Hovered : O
   void Draw() { /* ... */ }
```

when MouseIn:

```d
   ...
   o.to!State_Hovered();
   ...
```

when MouseOut:
```d
   ...
   o.to!State_Init();
   ...
```

It works! But I want to ask how to make this 100% the best of the 
best?
What should I consider before changing ```__vptr``` ?
Sep 17 2023
next sibling parent Vitaliy Fadeev <vital.fadeev gmail.com> writes:
On Sunday, 17 September 2023 at 15:05:59 UTC, Vitaliy Fadeev 
wrote:
 ...
Playground: https://run.dlang.io/is/hjcLCk
Sep 17 2023
prev sibling next sibling parent reply evilrat <evilrat666 gmail.com> writes:
On Sunday, 17 September 2023 at 15:05:59 UTC, Vitaliy Fadeev 
wrote:
 It works! But I want to ask how to make this 100% the best of 
 the best?
 What should I consider before changing ```__vptr``` ?
If that works for you with that constraint of having exact memory layout then it should be ok. This however is very uncommon pattern and your library users might reject it so keep that in mind if you are going to make public library. Other than that I would suggest at least to make that cast method to return a shallow copy because messing with "this" ptr can be dangerous (make sure to try it with const objects and optimized release builds before using this everywhere). An even better (at least safer, in theory) option would be to make "View" or handle struct that wraps an object(pointer) and tracks such transformations. Of course to think of it now there is yet another opportunity - why not to look for something like ECS then? Because you seem to already treat your objects purely as data containers, that way you can safely detach data from logic and reduce the scope of your components to keep them focused on one task. That is up to you of course.
Sep 17 2023
parent Johan <j j.nl> writes:
On Sunday, 17 September 2023 at 17:10:16 UTC, evilrat wrote:
 On Sunday, 17 September 2023 at 15:05:59 UTC, Vitaliy Fadeev 
 wrote:
 It works! But I want to ask how to make this 100% the best of 
 the best?
 What should I consider before changing ```__vptr``` ?
If that works for you with that constraint of having exact memory layout then it should be ok.
No, this is Undefined Behavior and will likely cause you trouble in the future (as in: some very hard to debug bugs may appear). Better to store the state in the object and select the behavior using a switch on the state. -Johan
Sep 17 2023
prev sibling next sibling parent reply Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Sunday, 17 September 2023 at 15:05:59 UTC, Vitaliy Fadeev 
wrote:
 Hi!
 I want to change a method ```Draw``` on a custom object when 
 the ```MouseIn``` event occurs.
 This is known as "Change State" of the object: ```Init``` -> 
 ```Hovered```.

 [...]
Interesting, but why would you want to do it that way? 😳
Sep 21 2023
parent reply Vitaliy Fadeev <vital.fadeev gmail.com> writes:
On Thursday, 21 September 2023 at 18:19:47 UTC, Imperatorn wrote:
 On Sunday, 17 September 2023 at 15:05:59 UTC, Vitaliy Fadeev 
 wrote:
 Hi!
 I want to change a method ```Draw``` on a custom object when 
 the ```MouseIn``` event occurs.
 This is known as "Change State" of the object: ```Init``` -> 
 ```Hovered```.

 [...]
Interesting, but why would you want to do it that way? 😳
Q & A. You can check the logic.
 How to increase the battery life of a smartphone?
Reducing operations.
How to change the state of a button widget, for example, on 
mouseover?
By changing the pointer to the Draw method.
 In addition to Draw, the widget has a Sense method. How to 
 replace all pointers to methods at once?
Replace the pointer with a class. (It's like a change of state! Exactly Turing's State Machine.) The object fields are the same. Behavior changes. The contents of the fields remain in memory. Only the pointer to the method table changes. Of course, this requires care and forethought. Perhaps the risks of changing class can be reduced by performing additional checks. Changing class is a convenient tool. I want to use it.
Sep 21 2023
parent reply Vitaliy Fadeev <vital.fadeev gmail.com> writes:
On Friday, 22 September 2023 at 02:51:10 UTC, Vitaliy Fadeev 
wrote:
 ...
``` Chip id name Sense() Draw() ``` instance ``` chip = new Chip(); ``` compiled to ``` chip __vtbl -------------> Chip __monitor Sense() id Draw() name ``` I want ``` chip __vtbl --+ id | name | |-> Chip_Hovered | Sense() | Draw() | +-> Chip_Hovered Sense() Draw() ```
Sep 21 2023
next sibling parent Vitaliy Fadeev <vital.fadeev gmail.com> writes:
On Friday, 22 September 2023 at 03:33:08 UTC, Vitaliy Fadeev 
wrote:
 On Friday, 22 September 2023 at 02:51:10 UTC, Vitaliy Fadeev 
 wrote:
 ...
the most correct ``` chip __vtbl ---+ // one of __monitor | id | name | |-> Chip // init | Sense() | Draw() | |-> Chip_Hovered // on mouseover | Sense() | Draw() | +-> Chip_Selected // on mouseclick Sense() Draw() ```
Sep 21 2023
prev sibling parent reply Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Friday, 22 September 2023 at 03:33:08 UTC, Vitaliy Fadeev 
wrote:
 On Friday, 22 September 2023 at 02:51:10 UTC, Vitaliy Fadeev 
 wrote:
 ...
``` Chip id name Sense() Draw() ``` instance ``` chip = new Chip(); ``` compiled to ``` chip __vtbl -------------> Chip __monitor Sense() id Draw() name ``` I want ``` chip __vtbl --+ id | name | |-> Chip_Hovered | Sense() | Draw() | +-> Chip_Hovered Sense() Draw() ```
What I mean is, why not use other language constructs like mixins or inheritance with some mapping for example?
Sep 22 2023
parent reply Vitaliy Fadeev <vital.fadeev gmail.com> writes:
On Friday, 22 September 2023 at 12:53:28 UTC, Imperatorn wrote:
 On Friday, 22 September 2023 at 03:33:08 UTC, Vitaliy Fadeev 
 wrote:
 On Friday, 22 September 2023 at 02:51:10 UTC, Vitaliy Fadeev 
 wrote:
 ...
``` Chip id name Sense() Draw() ``` instance ``` chip = new Chip(); ``` compiled to ``` chip __vtbl -------------> Chip __monitor Sense() id Draw() name ``` I want ``` chip __vtbl --+ id | name | |-> Chip_Hovered | Sense() | Draw() | +-> Chip_Hovered Sense() Draw() ```
What I mean is, why not use other language constructs like mixins or inheritance with some mapping for example?
Can you give an example?
Sep 22 2023
parent reply Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Friday, 22 September 2023 at 14:03:40 UTC, Vitaliy Fadeev 
wrote:
 On Friday, 22 September 2023 at 12:53:28 UTC, Imperatorn wrote:
 On Friday, 22 September 2023 at 03:33:08 UTC, Vitaliy Fadeev 
 wrote:
 [...]
What I mean is, why not use other language constructs like mixins or inheritance with some mapping for example?
Can you give an example?
You're basically just describing polymorphism. I can post an example tomorrow, it's midnight here now.
Sep 22 2023
parent Vitaliy Fadeev <vital.fadeev gmail.com> writes:
On Friday, 22 September 2023 at 21:37:37 UTC, Imperatorn wrote:
 On Friday, 22 September 2023 at 14:03:40 UTC, Vitaliy Fadeev 
 wrote:
 On Friday, 22 September 2023 at 12:53:28 UTC, Imperatorn wrote:
You're basically just describing polymorphism. I can post an example tomorrow, it's midnight here now.
Thank you. Of course! It's interesting to look at the solutions to choose the best one.
Sep 22 2023
prev sibling parent reply =?UTF-8?Q?Christian_K=c3=b6stlin?= <christian.koestlin gmail.com> writes:
On 17.09.23 17:05, Vitaliy Fadeev wrote:
 Hi!
 I want to change a method ```Draw``` on a custom object when the 
 ```MouseIn``` event occurs.
 This is known as "Change State" of the object: ```Init``` -> ```Hovered```.
 
 I want to change the state of an object by changing its class, like this:
 
 ```d
 
 this.__vptr = typeid(CLS).vtbl.ptr;
 
 ```
 
 I have read the ABI and am confident in replacing ```__vptr``` as long 
 as the classes contain the same fields and the same interfaces.
 
 Example:
 ```d
 // O
 //   to!state
 // State_Init    : O
 //   Draw
 // State_Hovered : O
 //   Draw
 //
 // o.to!State_Hovered
 // o.to!State_Init
 
 class O
 {
    void to(CLS)()
    {
      // if (same fields && same interfaces && same instance size)
      this.__vptr =
        cast(immutable(void*)*)typeid(CLS).vtbl.ptr;
    }
 }
 
 State_Init : O
    void Draw() { /* ... */ }
 
 State_Hovered : O
    void Draw() { /* ... */ }
 ```
 
 when MouseIn:
 
 ```d
    ...
    o.to!State_Hovered();
    ...
 ```
 
 when MouseOut:
 ```d
    ...
    o.to!State_Init();
    ...
 ```
 
 It works! But I want to ask how to make this 100% the best of the best?
 What should I consider before changing ```__vptr``` ?
 
You could model it oop style like this: https://run.dlang.io/is/MJb5Fk This solution might not be to your taste, as it involves interfaces, and classes and objects and garbage (all the news) ... another option could be to model your own VTable in a struct like this: https://run.dlang.io/is/3LTjP5 Kind regards, Christian
Sep 22 2023
next sibling parent reply Vitaliy Fadeev <vital.fadeev gmail.com> writes:
On Friday, 22 September 2023 at 19:50:17 UTC, Christian Köstlin 
wrote:
 another option could be to model your own VTable in a struct 
 like this:
 https://run.dlang.io/is/3LTjP5

 Kind regards,
 Christian
Thank, Christian ! True nice tasty solution with ```VTable```! And further... the project is growing. ``` void Draw() { DrawBG(); DrawFG(); } ``` And we want use ```DrawBG()``` code from ```initial``` in other states, like ```Selected```. How to use some functions from ```initial``` via ```VTable``` ? I see solution in ```classes``` and methods with ```override``` keyword. ```VTable``` does the same thing as ```__vptr``` ?
Sep 22 2023
parent =?UTF-8?Q?Christian_K=c3=b6stlin?= <christian.koestlin gmail.com> writes:
On 23.09.23 05:11, Vitaliy Fadeev wrote:
 On Friday, 22 September 2023 at 19:50:17 UTC, Christian Köstlin wrote:
 another option could be to model your own VTable in a struct like this:
 https://run.dlang.io/is/3LTjP5

 Kind regards,
 Christian
Thank, Christian ! True nice tasty solution with ```VTable```! And further... the project is growing. ``` void Draw() {   DrawBG();   DrawFG(); } ``` And we want use ```DrawBG()``` code  from ```initial``` in other states, like ```Selected```. How to use some functions from ```initial``` via ```VTable``` ? I see solution in ```classes``` and methods with ```override``` keyword. ```VTable``` does the same thing as ```__vptr``` ?
VTable is your structure .. it does exactly what you want it to do. __vptr is the internal implementation of virtual methods in the dlang object model. Line 20 and 21 in my example initialize the two `VTable`s Initial and Hovered. You can change VTable to contain two function pointers and initialize those as you like for the instances of the VTable structs. e.g. ```d struct DrawVTable { void function(Chip, Renderer) background; void function(Chip, Renderer) foreground; } // define functions to draw the different fore and backgrounds ... ... VTable initial = VTable(&drawInitialBackground, &drawInitialForeground); VTable hovered = VTable(&drawHoveredBackground, &drawHoveredForeground); VTable selected = VTable(&drawInitialBackground, &drawHoveredForegtround); ``` Kind regards, Christian
Sep 22 2023
prev sibling parent reply Vitaliy Fadeev <vital.fadeev gmail.com> writes:
On Friday, 22 September 2023 at 19:50:17 UTC, Christian Köstlin 
wrote:
 On 17.09.23 17:05, Vitaliy Fadeev wrote:
 Hi!
 
You could model it oop style like this: https://run.dlang.io/is/MJb5Fk This solution might not be to your taste, as it involves interfaces, and classes and objects and garbage (all the news) ... another option could be to model your own VTable in a struct like this: https://run.dlang.io/is/3LTjP5 Kind regards, Christian
```Behavior``` is beautiful code! But it contains a second ```new``` when ```Chip``` is created. One ```new``` is possible? Christian, really nice code! Does ```__vptr``` do the same thing ?
Sep 22 2023
parent =?UTF-8?Q?Christian_K=c3=b6stlin?= <christian.koestlin gmail.com> writes:
On 23.09.23 05:25, Vitaliy Fadeev wrote:
 On Friday, 22 September 2023 at 19:50:17 UTC, Christian Köstlin wrote:
 On 17.09.23 17:05, Vitaliy Fadeev wrote:
 Hi!
You could model it oop style like this: https://run.dlang.io/is/MJb5Fk This solution might not be to your taste, as it involves interfaces, and classes and objects and garbage (all the news) ... another option could be to model your own VTable in a struct like this: https://run.dlang.io/is/3LTjP5 Kind regards, Christian
```Behavior``` is beautiful code! But it contains a second ```new``` when ```Chip``` is created. One ```new``` is possible? Christian, really nice code!
Here a solution with less `new`s: https://run.dlang.io/is/iV1qVq. It really depends on the program that you are doing if creating those news is a problem. Kind regards, Christian
Sep 22 2023