Wiki page eo-inherit.md changed with summary [] by Nate Drake

This commit is contained in:
Nate Drake 2017-11-21 07:09:02 -08:00 committed by apache
parent 0db38ecdd4
commit c1b10a6508
1 changed files with 29 additions and 28 deletions

View File

@ -12,11 +12,11 @@ This tutorial shows how to inherit from a class in Eolian. It also describes how
## Prerequisites ##
* This tutorial builds on top of the ``Example.Rectangle`` class developed in [Creating New Classes with Eolian](eo-classes.md).
* The [Hello World](hello-world.md) tutorial explains how to write an application using the EFL.
* The [Hello World](hello-world.md) tutorial explains how to write an application using EFL.
## Step One: Creating a Derived Class ##
Copy all the ``example_rectangle.*`` files you created in the [Creating New Classes with Eolian](eo-classes.md) tutorial. There should be 4 of them: The Eolian file (``.eo``), the implementation file (``.c``) and two autogenerated files (``.eo.h`` and ``.eo.c``). Also copy the main file (``eo_classes_main.c``) and rename it to ``eo_inherit_main.c``, for consistency with the name of this tutorial.
Copy all the ``example_rectangle.*`` files you created in the [Creating New Classes with Eolian](eo-classes.md) tutorial. There should be 4 of them: The Eolian file (``.eo``), the implementation file (``.c``) and two autogenerated files (``.eo.h`` and ``.eo.c``). Make sure to copy the main file (``eo_classes_main.c``) and rename it to ``eo_inherit_main.c`` for consistency with the name of this tutorial.
Now you will create a new class, named ``Example.Square`` which will inherit from ``Example.rectangle``. The theory states that squares are a particular kind of rectangles in which the width and the height are equal. Therefore, the ``Example.Square`` class will *override* ``Example.Rectangle``'s ``width`` and ``height`` setters to ensure that those two variables have always the same value.
@ -31,11 +31,11 @@ class Example.Square (Example.Rectangle) {
}
```
As you can see, it is derived from ``Example.Rectangle``. Regular classes must derive from the ``Efl.Object``, but in this case, you get that automatically, since ``Example.Rectangle`` already derives from ``Efl.Object``.
As you can see this is derived from ``Example.Rectangle``. Regular classes must be derived from ``Efl.Object`` but in this case you get that automatically, since ``Example.Rectangle`` already is already derived from ``Efl.Object``.
You can also notice that this class does not provide any new method or property (there is no ``methods`` block). It only implements two properties, which currently belong to its parent class (``Example.Rectangle.width`` and ``Example.Rectangle.height``). Furthermore, only the setters for these properties are implemented: Reads of these properties will be routed to the getter in the parent class.
You can also notice that this class does not provide any new method or property (there is no ``methods`` block). It only implements two properties, which currently belong to its parent class (``Example.Rectangle.width`` and ``Example.Rectangle.height``). Furthermore only the setters for these properties are implemented: Reads of these properties will be routed to the getter in the parent class.
Next, turn the Eolian description into C files with the ``eolian_gen`` command (as seen in the previous tutorial). Be careful, though, there is a new parameter in use in this example:
Next turn the Eolian description into C files with the ``eolian_gen`` command (as seen in the previous tutorial). Be careful, as there's a new parameter in use in this example:
```bash
eolian_gen -gchi example_square.eo -I .
@ -43,13 +43,13 @@ eolian_gen -gchi example_square.eo -I .
The ``-I`` parameter tells ``eolian_gen`` where to look for other Eolian files, in case it needs the description of a class it does not know about. In this tutorial, ``Example.Square`` needs the description of ``Example.Rectangle`` which resides in the ``example_rectangle.eo`` file. This file is in the same folder as ``example_square.eo``, the current folder, therefore, the final command requires a ``-I .`` (The dot indicates the current folder).
This will create the boilerplate files (``.eo.h`` and ``.eo.c``) and the implementation file which you will fill in the next step.
This will create the boilerplate files (``.eo.h`` and ``.eo.c``) and the implementation file, which you will complete in the next step.
## Step Two: Implementing the Derived Class ##
Edit the implementation file ``example_square.c``. It should contain:
* An empty structure ``Example_Square_Data``. It will remain empty, because squares do not add any additional data to rectangles.
* An empty structure ``Example_Square_Data``. This will remain empty, because squares do not add any additional data to rectangles.
* A method called ``_example_square_example_rectangle_width_set()``. This is the setter for the ``width`` property, inherited from the parent ``Example.Rectangle`` class. The name of the method contains all the ancestry information. Don't worry, though, you will not be calling this method directly.
* A method called ``_example_square_example_rectangle_height_set()``. As above, this is the setter for the ``height`` property.
@ -59,9 +59,9 @@ The implementation of these methods requires calling the parent class, therefore
#include "example_rectangle.eo.h"
```
Time to think now about the implementation of these setters. They both need to set the ``width`` and ``height`` internal variables to the same value. But these variables are private to ``Example.Rectangle``, so ``Example.Square`` has no direct access to them. They can be accessed through the setters from ``Example.Rectangle``, though. Only care has to be taken not to end up calling the setter from ``Example.Square`` currently being implemented, or an infinite loop would be created.
You now need to focus on implementating these setters. They both need to set the ``width`` and ``height`` internal variables to the same value. These variables are private to ``Example.Rectangle``, so ``Example.Square`` has no direct access to them. These can be accessed through the setters from ``Example.Rectangle`` however. The setter has not been called from the ``Example.Square`` currently being implemented, as this would create an infinite loop.
In EFL, when you need to call a method from your parent instead of your overridden version, you can use ``efl_super()``. Its first parameter is the object and the second is *the class whose parent you want to call*. Write these implementations for the setters and it all will become apparent:
In EFL when you need to call a method from your parent instead of your overridden version you can use ``efl_super()``. Its first parameter is the object and the second is *the class whose parent you want to call*. Write these implementations for the setters and all will become clear:
```c
EOLIAN static void
@ -79,16 +79,16 @@ _example_square_example_rectangle_height_set(Eo *obj, Example_Square_Data *pd EI
}
```
Things worth noticing:
Note:
* The ``width`` and ``height`` variables are set through the regular setters you already used in the previous tutorial (``example_rectangle_width_set()`` and ``example_rectangle_height_set()``).
* The object being passed to the setters, though, is the output of ``efl_super()``.
* You want to call the parent of ``Example.Square``, so the second parameter to ``efl_supper()`` is ``EXAMPLE_SQUARE_CLASS``.
* These setters do not use the ``Example_Square_Data`` private data (that structure is actually empty, as seen above), so ``EINA_UNUSED`` is used to avoid compiler warnings.
The ``efl_super()`` method can also be used to access older ancestors of your class, but that's an advanced scenario not required in this tutorial.
The ``efl_super()`` method can also be used to access older ancestors of your class but this is a complex operation and so is outside the scope of this tutorial.
Having written these setters, your derived ``Example.Square`` class is finished. The next step adds code that uses it.
Having written these setters your derived ``Example.Square`` class is complete. The next step adds code that uses it.
## Step Three: Using the Derived Class ##
@ -114,15 +114,15 @@ _square_create()
}
```
Observe how the side of the rectangle is set with ``example_rectangle_width_set()``. You could also set the ``height``, since both setters have the same effect on a square.
Note how the side of the rectangle is set with ``example_rectangle_width_set()``. You could also set the ``height`` since both setters have the same effect on a square.
Back in the main function, declare a variable to hold your new object:
Back in the main function declare a variable to hold your new object:
```c
Eo *rectangle, *square;
```
Now call ``_square_create()`` and print some information (right after doing the same thing for the rectangle object):
Next call ``_square_create()`` and print some information, immediately after doing the same thing for the rectangle object:
```c
square = _square_create();
@ -135,9 +135,9 @@ Now call ``_square_create()`` and print some information (right after doing the
efl_unref(square);
```
Notice how you only used ``Example.Rectangle`` methods here, because ``Example.Square`` inherits from it, and all methods that work on a rectangle also work on a square.
Notice how you only used ``Example.Rectangle`` methods here because ``Example.Square`` inherits from it. All methods that work on a rectangle also work on a square.
Also, remember to dispose of your objects using ``efl_unref()`` if you do not give them a parent in ``efl_add()``.
Remember to dispose of your objects using ``efl_unref()`` if you do not give them a parent in ``efl_add()``.
The main program (``eo_inherit_main.c``) should now look like this:
@ -203,7 +203,7 @@ efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
EFL_MAIN()
```
If you run it, you should get this on your terminal:
If you run now, you should see this in your terminal:
```bash
Rectangle is 5x10, area is 50
@ -212,9 +212,9 @@ Square is 7x7, area is 49
## Step Four: Accessing the Parent's Private Data ##
The above implementation for ``Example.Square``'s setters works because ``Example.Rectangle`` has public setters. That is, even though ``width`` and ``height`` are private variables visible only to ``Example.Rectangle`` they can be accessed by anyone through their setters and getters.
The above implementation for ``Example.Square``'s setters works because ``Example.Rectangle`` has public setters. That is to say, even though ``width`` and ``height`` are private variables visible only to ``Example.Rectangle``, they can be accessed by anyone through their setters and getters.
The last step in this tutorial shows how a derived class can access private data from its parent, which is also a common operation in OOP.
The final step in this tutorial shows you how a derived class can access private data from its parent, which is also a common operation in OOP.
The first thing done in the implementation file ``example_rectangle.c`` is to define the ``Example_Rectangle_Data`` structure, which is therefore only accessible from that file. If ``Example.Square`` has to have access to this structure, it has to be defined in a common header.
@ -233,7 +233,7 @@ In ``example_rectangle.c``, replace the structure with an include :
#include "example_rectangle_private.h"
```
Finally include the header *also* from ``example_square.c``. At this point, the implementation for ``Example.Square`` can understand the private data of its parent ``Example.Rectangle``. You only need to retrieve a pointer to that data, using ``efl_data_scope_get()``. This is how your square setters should look now:
Finally include the header from ``example_square.c`` too. At this point the implementation for ``Example.Square`` can interpret its parent's private data ``Example.Rectangle``. You only need to retrieve a pointer to that data using ``efl_data_scope_get()``. This is how your square setters should look now:
```c
EOLIAN static void
@ -247,24 +247,25 @@ _example_square_example_rectangle_width_set(Eo *obj, Example_Square_Data *pd EIN
And likewise for the ``height`` setter.
Notice how the first parameter to ``efl_data_scope_get()`` is the object for which you want to retrieve the private data, and the second parameter is the *ancestor class*. You can retrieve the private data for any class, as long as it belongs to your hierarchy.
Note how the first parameter to ``efl_data_scope_get()`` is the object for which you want to retrieve the private data, and the second parameter is the *ancestor class*. You can retrieve the private data for any class, as long as it belongs to your hierarchy.
> **NOTE:**
> For performance reasons, no runtime check is performed to ensure that the requested class actually belongs to your ancestry. If you want to avoid *undefined behavior* use ``efl_data_scope_safe_get()``.
**NOTE:**
For performance reasons, no runtime check is performed to ensure that the requested class actually belongs to your ancestry. If you want to avoid *undefined behavior* use ``efl_data_scope_safe_get()``.
Once you have the pointer to the private data of ``Example.Rectangle`` you can write to both ``width`` and ``height`` as you were doing before.
Finally, if you need to keep this private data pointer alive for a long time, it is worth reading about ``efl_data_ref()`` and ``efl_data_unref()``. These methods will make sure that the enclosing object is not destroyed until you are done working with its private data.
If you need to keep the private data pointer alive for a long time look into ``efl_data_ref()`` and ``efl_data_unref()``. These methods will make sure that the enclosing object is not destroyed until you are done working with its private data.
## Step Five: Per-Object Method Override ##
One final function worth knowing is ``efl_object_override()``. It allows changing some method's implementation for a particular object, just like derived classes do, but on a single object.
One final function worth knowing is ``efl_object_override()``. It allows changing some method's implementation for a particular object just as derived classes do but on a single object.
Its use case is a bit advanced so it will not be shown in this tutorial. It is related to class inheritance, though, so it deserves being mentioned.
Its use case is a bit advanced so it will not be shown in this tutorial. It is related to class inheritance however so is worthy of note.
## Summary ##
This tutorial has taught you:
In this tutorial, you've learned:
* **Derived classes** can be created with Eolian.
* **Methods overridden by the derived class** are automatically called when a derived class object is used.