summaryrefslogtreecommitdiff
path: root/doc/Eo_Tutorial
blob: 766ed1ed6ec9f755ff196ac558757e5bac0d5d53 (plain)
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
EO Tutorial:
===========

* Purpose:
---------
The purpose of this document is to explain how to work with Eo, how to port your code to Eo and what are the common pitfalls. It doesn't explain how it works inside.

* Description:
-------------
Eo is an Object oriented infrastructure for the EFL. It is a API/ABI safe library.

It supports inheritance, mixins, interfaces and composite objects.
Every class can implement functions from every other class.
It supports event signals, function overrides, private/protected/public/etc. variables and functions.

At the creation of the class, a "virtual table" is filled with the needed functions.
The key of this table is a (class id, function id) tuple.

eo_do is invoked with a list of op ids and their parameters and is in charge to dispatch the relevant functions. Finding the correct function is fast because it is just a lookup table.

* How to use it?
---------------
1) Creation of an instance of a class
Old way:
object = evas_object_line_add(canvas);

New way:
object = eo_add(EVAS_OBJ_LINE_CLASS, canvas);

2) Call to function
Old way:
   evas_object_move(obj, 120, 120);
   evas_object_resize(obj, 200, 200);

Eo way:
   eo_do(obj,
         evas_obj_move(120, 120),
         evas_obj_resize(200, 200));

3) Extract specific data
Old way:
   Evas_Object_Line *o = (Evas_Object_Line *)(obj->object_data);
   MAGIC_CHECK(o, Evas_Object_Line, MAGIC_OBJ_LINE);
   return;
   MAGIC_CHECK_END();

Eo way:
   Evas_Object_Line *o = eo_data_get(obj, EVAS_OBJ_LINE_CLASS);
   if (!o) return;

4) Call function of parent
Old way:
   ELM_WIDGET_CLASS(_elm_button_parent_sc)->theme(obj))

New way:
   eo_do_super(obj, elm_wdg_theme(&int_ret));

* Important to know:
-------------------
1) eo_do is the function used to invoke functions of a specific class on an object.

2) eo_data_get receives an object and a class and returns the data of the given class for the object. The class must belong to the object class hierarchy.

3) eo_isa indicates if a given object is of a given type.

4) eo_do_super is in charge to invoke a function in the next parents that implements it. It is recommended to use eo_do_super only from a function with the same op id.
In addition, there is no way to jump over classes who implement the function. If A inherits from B, B from C and A, B and C implement a virtual function defined in C, the function calls order will be A, then B and finally C. It is impossible to pass over B.

5) eo_do returns if the operation succeeded or failed (function found, object deleted...), not the result of the called function. Pay attention to this detail when you call eo_do. The return value needs to be an additional parameter which will hold a return value.

6) Don't do this:
   int w, h;
   eo_do(obj,
         evas_obj_size_get(&w, &h),
         evas_obj_size_set(w+10, h+20));
   w+10 and h+20 are evaluated before the call to size_get.
   Instead, separate it in two calls to eo_do.

7) When calling eo_add on an object, the reference counter of this one is incremented. You need to call eo_unref to decrement this counter.
If it is called with a parent, two references are on the object. eo_del removes both of these references.
When there is no more references on an object, this one is deleted and then can be freed. The deletion calls to the destructor (see eo_destructor). When done, Eo checks if the object can be freed. The free mechanism can be "disabled" through eo_manual_free_set. It is the responsibility of the developer to call eo_manual_free on the object to free it. This mechanism has been used for example in Evas on the Evas objects and in Ecore

8) When eo_do reaches a function of a class, it is the responsibility of the user to extract from the va_list ALL the parameters needed for this function, NO MORE, NO LESS. Otherwise, unknown behavior can occur. eo_do is called with a list of op id, params, op id, params... A bad extraction of parameters can bring to the parsing of a wrong op id and so in the best case, to an error, in the worst case, to another function not in relation with the actual use case.

9) Always pay attention to:
   - the pairing between function id and the function itself. Using the same function for two ids occurs and is hard to debug.
   - the definition of the function macros in the H file and their parameters order/type
   - to extract all the parameters of a function from the va_list

10) Enum of the op ids in H file and descriptions in C file must be synchronized, i.e same number of ids and same order. If you change the order by adding, removing or moving ids, you break ABI of your library.

11) Avoid exposing your class data to prevent ABI break. Supply access functions instead.

* How to create a class - H side?
--------------------------------
- If the object is new, establish the public APIs
- #define $(CLASS_NAME) $(class_name)_class_get(): will be used to access data/inherit from this class...
- const Eo_Class *$(class_name)_class_get(void) EINA_CONST: declaration of the function that will create the class (not the instance), i.e virtual table...
- extern EAPI Eo_Op $(CLASS_NAME)_BASE_ID: class id that will be essentially used to identify functions set of this class
- enum of the function ids of the class in the form $(CLASS_NAME)_SUB_ID: used to identify the function inside the class; function id is unique per class but (class id, function id) is unique per system..
- #define $(CLASS_NAME)_ID(sub_id) ($(CLASS_NAME)_BASE_ID + sub_id): formula to calculate the system function id
- define of each function consists of:
   - the name of the function that will be used in eo_do
   - parameters without types
   - $(CLASS_NAME)_ID($(CLASS_NAME)_SUB_ID_FUNCTION
   - EO_TYPECHECK for each parameter: type and variable name
- And don't forget to document each function

Evas Object Line Example:
------------------------
#define EVAS_OBJ_LINE_CLASS evas_object_line_class_get()
const Eo_Class *evas_object_line_class_get(void) EINA_CONST;

extern EAPI Eo_Op EVAS_OBJ_LINE_BASE_ID;

enum
{
   EVAS_OBJ_LINE_SUB_ID_XY_SET,
   EVAS_OBJ_LINE_SUB_ID_XY_GET,
   EVAS_OBJ_LINE_SUB_ID_LAST
}

#define EVAS_OBJ_LINE_ID(sub_id) (EVAS_OBJ_LINE_BASE_ID + sub_id)

/**
 * @def evas_obj_line_xy_set
 * @since 1.8
 *
 * Sets the coordinates of the end points of the given evas line object.
 *
 * @param[in] x1
 * @param[in] y1
 * @param[in] x2
 * @param[in] y2
 *
 * @see evas_object_line_xy_set
 */
#define evas_obj_line_xy_set(x1, y1, x2, y2) EVAS_OBJ_LINE_ID(EVAS_OBJ_LINE_SUB_ID_XY_SET), EO_TYPECHECK(Evas_Coord, x1), EO_TYPECHECK(Evas_Coord, y1), EO_TYPECHECK(Evas_Coord, x2), EO_TYPECHECK(Evas_Coord, y2)

/**
 * @def evas_obj_line_xy_get
 * @since 1.8
 *
 * Retrieves the coordinates of the end points of the given evas line object.
 *
 * @param[out] x1
 * @param[out] y1
 * @param[out] x2
 * @param[out] y2
 *
 * @see evas_object_line_xy_get
 */
#define evas_obj_line_xy_get(x1, y1, x2, y2) EVAS_OBJ_LINE_ID(EVAS_OBJ_LINE_SUB_ID_XY_GET), EO_TYPECHECK(Evas_Coord *, x1), EO_TYPECHECK(Evas_Coord *, y1), EO_TYPECHECK(Evas_Coord *, x2), EO_TYPECHECK(Evas_Coord *, y2)

* How to create a class - C side?
--------------------------------
Below, the object line as example.

#include "Eo.h"
EAPI Eo_Op $(CLASS_NAME)_BASE_ID = EO_NOOP; // Initialisation of the class id to 0. Will be set dynamically by Eo itself.
#define MY_CLASS $(CLASS_NAME)

...

Example for a developer function called by Eo:
This function receives the Eo object, the data corresponding to this class and the list of parameters.
static void
_foo(Eo *eo_obj, void *_pd, va_list *list)
{
   int param_1 = va_arg(*list, int);
   Eina_Bool param_2 = va_arg(*list, int);
   Eina_Bool ret = va_arg(*list, Eina_Bool *);
   foo_data obj = _pd;

   if (ret) *ret = EINA_FALSE;
   ...
}

You can (not a must) implement a constructor. This constructor MUST call the parent constructor (eo_do_super). It is the same for the destructor.
See eo_constructor and eo_destructor.
If you don't have anything to do in constructor (like malloc, variables init...) or in destructor (free), don't implement them.

At the end of the file, you need to describe the class.
First, you need to supply the class constructor that sets the list of the functions that are implemented in this class. It includes functions overriden (constructor, destructor, member_add...) and functions specific to this class. If there is no function implemented in this class, you don't need a class constructor.
Then, you need a list to describe the new functions. For each one, the op id and a description.
Then, the class itself that consists in:
   - the version of Eo, used for backward compatibility
   - the class name
   - the type of the class:
      - regular: an object can be created from this type (mostly used)
      - regular non instant-iable: Same as previous, but objects can’t be created from this type (elm_widget)
      - interface: used to extend classes, no implementations for functions and no internal data
      - mixin : interfaces with internal data and pre-made implementations for functions
   - the descriptions list described above and the number of ops for this class
   - events
   - size of the data of the class
   - the class constructor, if exists, else NULL
   - the class destructor (NULL most of the time)

Finally, we define the class with:
   - the function name to give to the function that creates/returns the class
   - the class description explained above
   - the parent and the interfaces/mixins, finished by NULL


Evas Object Line Example:
------------------------
#include "Eo.h"
EAPI Eo_Op EVAS_OBJ_LINE_BASE_ID = EO_NOOP;
#define MY_CLASS EVAS_OBJ_LINE_CLASS

...

static void
_line_xy_get(Eo *eo_obj, void *_pd, va_list *list)
{
   const Evas_Object_Line *o = _pd;

   Evas_Coord *x1 = va_arg(*list, Evas_Coord *);
   Evas_Coord *y1 = va_arg(*list, Evas_Coord *);
   Evas_Coord *x2 = va_arg(*list, Evas_Coord *);
   Evas_Coord *y2 = va_arg(*list, Evas_Coord *);

   Evas_Object_Protected_Data *obj = eo_data_get(eo_obj, EVAS_OBJ_CLASS);
   if (x1) *x1 = obj->cur.geometry.x + o->cur.x1;
   if (y1) *y1 = obj->cur.geometry.y + o->cur.y1;
   if (x2) *x2 = obj->cur.geometry.x + o->cur.x2;
   if (y2) *y2 = obj->cur.geometry.y + o->cur.y2;
}

static void
_constructor(Eo *eo_obj, void *class_data, va_list *list EINA_UNUSED)
{
   eo_do_super(eo_obj, eo_constructor());

   Evas_Object_Protected_Data *obj = eo_data_get(eo_obj, EVAS_OBJ_CLASS);
   evas_object_line_init(eo_obj);
}

...

/* class constructor */
static void
_class_constructor(Eo_Class *klass)
{
   const Eo_Op_Func_Description func_desc[] = {
        /* Virtual functions of parent class implemented in this class */
        EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_CONSTRUCTOR), _constructor),
        EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_DESTRUCTOR), _destructor),
        /* Specific functions to this class */
        EO_OP_FUNC(EVAS_OBJ_LINE_ID(EVAS_OBJ_LINE_SUB_ID_XY_SET), _line_xy_set),
        EO_OP_FUNC(EVAS_OBJ_LINE_ID(EVAS_OBJ_LINE_SUB_ID_XY_GET), _line_xy_get),
        EO_OP_FUNC_SENTINEL
   };

   eo_class_funcs_set(klass, func_desc);
}

/* Descriptions for the functions specific to this class */
static const Eo_Op_Description op_desc[] = {
     EO_OP_DESCRIPTION(EVAS_OBJ_LINE_SUB_ID_XY_SET, "Sets the coordinates of the end points of the given evas line object."),
     EO_OP_DESCRIPTION(EVAS_OBJ_LINE_SUB_ID_XY_GET, "Retrieves the coordinates of the end points of the given evas line object."),
     EO_OP_DESCRIPTION_SENTINEL
};

/* Description of the class */
static const Eo_Class_Description class_desc = {
     EO_VERSION,
     "Evas_Object_Line",
     EO_CLASS_TYPE_REGULAR,
     EO_CLASS_DESCRIPTION_OPS(&EVAS_OBJ_LINE_BASE_ID, op_desc, EVAS_OBJ_LINE_SUB_ID_LAST),
     NULL,
     sizeof(Evas_Object_Line),
     _class_constructor,
     NULL
};

/* Definition of the class */
EO_DEFINE_CLASS(evas_object_line_class_get, &class_desc, EVAS_OBJ_CLASS, NULL);