summaryrefslogtreecommitdiff
path: root/pages/develop/tutorials/c/eo-inherit.md.txt
blob: 383e5ea7ea310b1c3c190347fb215013851f0cf6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
---
~~Title: Class Inheritance with Eolian~~
~~NOCACHE~~
---

# Class Inheritance with Eolian #

The [Creating New Classes](eo-classes.md) tutorial explained how to define new classes using Eolian. New classes don't need to start from scratch however. In fact, it is common practice in [Object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming) (OOP) to extend the functionality of an existing class by *inheriting* from it and creating a new one.

This tutorial shows how to inherit from a class in Eolian. It also describes how derived classes can access their parent's public and private data.

## 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 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``). 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.

Start describing your new class with a new Eolian file named ``example_square.eo``:

```
class Example.Square extends Example.Rectangle {
   implements {
      Example.Rectangle.width {set;}
      Example.Rectangle.height {set;}
   }
}
```

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`` 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.

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 .
```

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 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``. 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.

The implementation of these methods requires calling the parent class, therefore, you need to include the parent's class header file. Add a line after the first block of ``#include``s in the file:

```c
#include "example_rectangle.eo.h"
```

You now need to focus on implementing 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. However, they can be modified through the setters from ``Example.Rectangle``.

A small caveat, though: The parent's setters are being overridden by the derived class, so if you try to call them directly from the overridden code, you will end up in an infinite loop! Keep reading. 

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
_example_square_example_rectangle_width_set(Eo *obj, Example_Square_Data *pd EINA_UNUSED, int width)
{
   example_rectangle_width_set(efl_super(obj, EXAMPLE_SQUARE_CLASS), width);
   example_rectangle_height_set(efl_super(obj, EXAMPLE_SQUARE_CLASS), width);
}

EOLIAN static void
_example_square_example_rectangle_height_set(Eo *obj, Example_Square_Data *pd EINA_UNUSED, int height)
{
   example_rectangle_width_set(efl_super(obj, EXAMPLE_SQUARE_CLASS), height);
   example_rectangle_height_set(efl_super(obj, EXAMPLE_SQUARE_CLASS), height);
}
```

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 this is a complex operation and so is outside the scope of this tutorial. 

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 ##

Open up ``eo_inherit_main.c`` and start by adding the include for ``Example.Square``:

```c
#include "example_square.eo.h"
```

Add another method to instantiate your new class, right after ``_rect_create()``:

```c
Example_Square *
_square_create()
{
   Example_Square *square;

   square = efl_new(EXAMPLE_SQUARE_CLASS,
                    efl_name_set(efl_added, "Square"),
                    example_rectangle_width_set(efl_added, 7));

   return 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:

```c
   Eo *rectangle, *square;
```

Next call ``_square_create()`` and print some information, immediately after doing the same thing for the rectangle object:

```c
   square = _square_create();

   printf("Square is %dx%d, area is %d\n",
          example_rectangle_width_get(square),
          example_rectangle_height_get(square),
          example_rectangle_area(square));

   efl_unref(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.

Remember to dispose of your objects using ``efl_unref()``. Alternatively, you could give them a parent using ``efl_add()`` instead of ``efl_new()`` and let the parent handle object disposal.

The main program (``eo_inherit_main.c``) should now look like this:

```c
#define EFL_BETA_API_SUPPORT 1

#include <Eina.h>
#include <Efl_Core.h>
#include "example_rectangle.eo.h"
#include "example_square.eo.h"

Example_Rectangle *
_rect_create()
{
   Example_Rectangle *rectangle;

   rectangle = efl_new(EXAMPLE_RECTANGLE_CLASS,
                       efl_name_set(efl_added, "Rectangle"),
                       example_rectangle_width_set(efl_added, 5),
                       example_rectangle_height_set(efl_added, 10));

   return rectangle;
}

Example_Square *
_square_create()
{
   Example_Square *square;

   square = efl_new(EXAMPLE_SQUARE_CLASS,
                    efl_name_set(efl_added, "Square"),
                    example_rectangle_width_set(efl_added, 7));

   return square;
}

EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev EINA_UNUSED)
{
   Eo *rectangle, *square;

   rectangle = _rect_create();

   printf("Rectangle is %dx%d, area is %d\n",
          example_rectangle_width_get(rectangle),
          example_rectangle_height_get(rectangle),
          example_rectangle_area(rectangle));

   efl_unref(rectangle);

   square = _square_create();

   printf("Square is %dx%d, area is %d\n",
          example_rectangle_width_get(square),
          example_rectangle_height_get(square),
          example_rectangle_area(square));

   efl_unref(square);

   efl_exit(0);
}
EFL_MAIN()
```

If you run now, you should see this in your terminal:

```bash
Rectangle is 5x10, area is 50
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 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 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.

Create an ``example_rectangle_private.h`` file and move the structure there:

```c
typedef struct
{
   int width, height;
} Example_Rectangle_Data;
```

In ``example_rectangle.c``, replace the structure with an include :

```c
#include "example_rectangle_private.h"
```

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_Data``. 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
_example_square_example_rectangle_width_set(Eo *obj, Example_Square_Data *pd EINA_UNUSED, int width)
{
   Example_Rectangle_Data *rect_pd = efl_data_scope_get(obj, EXAMPLE_RECTANGLE_CLASS);
   rect_pd->width = width;
   rect_pd->height = width;
}
```

And likewise for the ``height`` setter.

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()``.

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.

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 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 however so is worthy of note.

## Summary ##

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.
* **Public data** of the parent class can be accessed through its **public accessors**.
* **Private data** of the parent class can be accessed through ``efl_data_scope_get()``.
* **Method implementations on individual objects** can be overridden using ``efl_object_override()``.

## Further Reading ##

[Creating New Classes](eo-classes.md)
:    Teaches the basis of class creation with Eo

[Multiple Inheritance](eo-multiinherit.md)
:    Moves forward and explains how to inherit from more than one class at a time