Project

General

Profile

Scol reflexive functions » History » Version 2

arkeon, 10/23/2014 07:39 PM
update with the callback api change

1 1 ModularMix
h1. Scol reflexive functions
2
3
The purpose of this document is to explain how a reflexive Scol function works, that is to say a callback function which can be called from Scol language.
4
In this tutorial, we will use the term *callback function* for the C++ function which be called when the event will occur, and the term *reflexive function* for the Scol function called by the callback function (so when the event occurs). In other terms, a reflexive function is comparable to a callback function which can be called in Scol language.
5
Our example will show how to call a reflexive function as soon as one the values of *Bloc* instance is updated.
6
7
h2. Creation of a callback for Bloc type
8
9
The constant *SCOL_BLOC_NEW_DATA_CB* correspond to the ID of the reflexive function in the data type *Bloc*. We can explain this principle as an ID of a slot in the data type *Bloc*, each slot corresponding to a specific reflexive function which can be defined (if a reflexive function is not defined in the slot of an instance of this object, the Scol Virtual machine will ignore this call).
10
11
The variable named *BLOC_NEW_DATA_CB* enables to store the ID of the Windows event which will trigger the call to the C++ callback.
12
<pre>
13
 //! New data event callback number
14
 int SCOL_BLOC_NEW_DATA_CB=0;
15
 
16
 //! New data event number
17
 int BLOC_NEW_DATA_CB;
18
</pre>
19
20
21
We will define our first callback function. Its purpose is to push the parameters used by the reflexive function (we'll define them later) into the Scol stack, an then call the reflexive function using a function from the Scol API named *OBJcallreflex*.
22
This function takes as parameters :
23
* 2 mandatory parameters for the reflexive function
24
** the object which has triggered the call (first parameter)
25
** a user parameter which can be of any type (second parameter, unused in our case)
26
* as many optional parameters as we need to use
27
28
Our custom parameters will respectively be the new integer value stored in Bloc object, et the new name of this object.
29
Consequently, we can already notice that the prototype of our Scol reflexive function will be : *fun [ObjBloc u0 I S] u1*.
30
31
The other important point concerns the function *OBJbeginreflex* from the Scol API. It can notice the Scol VM that we're preparing a call to a reflexive function.
32
Given that several of these functions can be defined in the VM for each data type, even for each object instance of this data type, it's used to specify the target of the call.
33
34
In our case, the reflexive function called which be the one from the slot *SCOL_BLOC_NEW_DATA_CB* of the object instance *bloc*, which Scol type is *OBJBLOCSCOL*.
35
<pre>
36
 /*!
37
 * \brief Callback function for calling scol reflex defined for BLOC_NEW_DATA_CB
38
 *
39
 * \param mmachine : scol machine structure
40
 * \param HWND : target window handle
41
 * \param unsigned msg : window message
42
 * \param UINT : callback param
43
 * \param LONG : callback param
44
 * \param int : return value
45
 */
46 2 arkeon
 int getBlocNewData(mmachine m, UINT id, LONG param)
47 1 ModularMix
 {
48
     int k = 0;
49
 
50
     // Cast id parameter to BlocObj type
51
     Bloc * bloc = (Bloc*) id;
52
 
53
     // Use : OBJbeginreflex(mmachine, type of object, ptr object, callback type)
54
     if (OBJbeginreflex(m, OBJBLOCSCOL, (int)bloc, SCOL_BLOC_NEW_DATA_CB))
55
     {
56
         MMechostr(MSKDEBUG,"Bloc not found\n");
57
         return 0;
58
     }
59
 
60
     // Retrieve current bloc value, and set it as 3rd parameter of the callback
61
     MMpush(m, ITOM(bloc->getValue()));
62
     // Retrieve current bloc name, and set it as 4th parameter of the callback
63
     Mpushstrbloc(m, bloc->getName());
64
 
65
     // Call reflex previously defined
66
     k = OBJcallreflex(m, 2 /*nb param after obj and u0*/);
67
     return k;
68
 }
69
</pre>
70
71
72
When the plugin is loading, we have to notify to the Scol VM that we want our reflexive function to be available on our object *Bloc*.
73
74
The call of the function enabling to register the new data type must be updated, because the first parameter of *OBJregister* corresponds to the number of callbacks which will be defined on the object. Then, we have to update this parameter to '1'.
75
76
In addition, we have to define a new event and associate to it the callback function. These 2 operations are done by calling the following functions from the Scol API :
77
* *OBJgetUserEvent* : returns an event ID which is available into the Scol VM,
78
* *OBJdefEvent* : associates this ID to a pointer on the callback function.
79
80
<pre>
81
 /*!
82
  * \brief Load the template functions
83
  *
84
  * \param mmachine : scol machine structure
85
  *
86
  * \return int : 0 if succes, error code otherwise
87
  **/
88
 int LoadTemplate(mmachine m)
89
 {
90
     int k;
91
 
92
     // Declare a new type of object ("OBJBLOCSCOL")
93
     OBJBLOCSCOL = OBJregister('''1 /*nb of callback*/''', 1/* deleted from parent */, destroyBlocObj, "OBJBLOCSCOL");
94
 
95
     // ----- Define callbacks
96
     // Get a new user event
97
     BLOC_NEW_DATA_CB = OBJgetUserEvent();
98
 
99
     // ----- Define callbacks for the call of the reflexive function.
100 2 arkeon
     OBJdefEvent(BLOC_NEW_DATA_CB, (int (__cdecl *)(struct Mmachine*, UINT, LONG))getBlocNewData);
101 1 ModularMix
 
102
     // Load package
103
     k = PKhardpak(m, "TemplateEngine", NbTplPKG, TplName, TplFunc, TplNArg, TplType);
104
     return k;
105
 }
106
</pre>
107
108
109
Then, we have to add the following code in the functions *_SETBlocValue* and *_SETBlocName* to trigger the new event.
110
<pre>
111 2 arkeon
 // send the change data callback message to scol, use OBJpostEvent instead if you need to poll the message
112
 OBJsendEvent(BLOC_NEW_DATA_CB,(int)bloc,(LONG)NULL);
113 1 ModularMix
</pre>
114
115
h2. Registration of a new reflexive function
116
117
In order to be able to call a reflexive function of a *Bloc* object, we first have to associate it to this instance.
118
The next function enables to register in the slot *SCOL_BLOC_NEW_DATA_CB* an object *OBJBLOCSCOL* in the Scol function which prototype is *fun [ObjBloc u0 I S] u1*, which will be called when *OBJcallreflex* will be run.
119
<pre>
120
 /*! @ingroup group1
121
 * \brief _CBblocChangeValue : This function sets the reflexive function to be executed when Bloc value change event happens
122
 *
123
 * <b>Prototype:</b> fun [BlocObj fun [ObjBloc u0 I S] u1 u0] BlocObj
124
 *
125
 * \param BlocObj : Bloc Object whose value has changed
126
 * \param fun [ObjBloc u0 I S] u1 : The reflexive function to call when the event occurs.
127
 * - I : int value of the object
128
 * - S   : name value of the object
129
 * \param u0 : User parameter
130
 *
131
 * \return BlocObj : The Bloc object whose value has changed
132
 */
133
 int _CBblocChangeValue(mmachine m)
134
 {
135
     // Add a reflex
136
     MMechostr(MSKDEBUG, "_CBblocChangeValue ...adding reflex\n");
137
     return OBJaddreflex(m, OBJBLOCSCOL, SCOL_BLOC_NEW_DATA_CB);
138
 }
139
</pre>
140
141
142
To allow the Scol developer to associate a reflexive function, we have to bind the function *_CBblocChangeValue* using the same method described above in the document.
143
Consequently, we will modify the variables used by *PKhardpak*. The prototype for the Scol function named *_CBblocChangeValue* is : *_fun [ObjBloc fun [ObjBloc u0 I S] u1 u0] ObjBloc*.
144
Note that the prototype of the reflexive function is the second parameter.
145
<pre>
146
 //! Nb of Scol functions or types
147
 #define NbTplPKG	9
148
 
149
 /*!
150
  * Scol function names
151
  **/
152
 char* TplName[NbTplPKG] =
153
 {
154
     "_HelloWorld",
155
     "ObjBloc",
156
     "_CRbloc",
157
     "_DSbloc",
158
     "_GETblocValue",
159
     "_GETblocName",
160
     "_SETblocName",
161
     "_SETblocValue",
162
     "_CBblocChangeValue"
163
 };
164
 
165
 /*!
166
  * Pointers to C functions that manipulate the VM for each scol function previously defined
167
  **/
168
 int (*TplFunc[NbTplPKG])(mmachine m)=
169
 {
170
     _HelloWorld,
171
     NULL,
172
     _CRbloc,
173
     _DSbloc,
174
     _GETblocValue,
175
     _GETblocName,
176
     _SETblocName,
177
     _SETblocValue,
178
     _CBblocChangeValue
179
 };
180
 
181
 /*!
182
  * Nb of arguments of each scol function
183
  **/
184
 int TplNArg[NbTplPKG]=
185
 {
186
     0,
187
     TYPTYPE,
188
     1,
189
     1,
190
     1,
191
     1,
192
     2,
193
     2,
194
     3
195
 };
196
 
197
 /*!
198
  * Prototypes of the scol functions
199
  **/
200
 char* TplType[NbTplPKG]=
201
 {
202
     "fun [] I",                                        // _HelloWorld
203
     NULL,
204
     "fun [Chn] ObjBloc",				// _CRbloc
205
     "fun [ObjBloc] I",					// _DSbloc
206
     "fun [ObjBloc] I",                                 // _GETblocValue
207
     "fun [ObjBloc] S",                                 // _GETblocName
208
     "fun [ObjBloc S] I",                               // _SETblocName
209
     "fun [ObjBloc I] I",                               // _SETblocValue
210
     "fun [ObjBloc fun [ObjBloc u0 I S] u1 u0] ObjBloc" //_CBblocChangeValue
211
 };
212
</pre>
213
214
215
h2. Use of a reflexive function in Scol
216
217
The purpose of the reflexive function is to log into the Scol console the updated values for the object *ObjBloc*. Let's add the following function into *template.pkg* file :
218
<pre>
219
 /*! \brief Callback that show values of an ObjBloc whenever a property he owned is changed.
220
  *
221
  *  Values are save to console log.
222
  *
223
  *  <b>Prototype:</b> fun [u0 u1 u1 u2] I
224
  *
225
  *  \param u0: not used, but the bloc instance where a value has changed is passed.
226
  *  \param u1: not used
227
  *  \param I : bloc value
228
  *  \param S : bloc name
229
  *   
230
  *  \return I : 0
231
  **/
232
 fun blocHasChanged(blocInstance, userParam, newValue, newName)=
233
   // Log new values
234
   _fooS strcatn "Bloc name:  "::newName::"\nBloc value: "::(itoa newValue)::nil;
235
   0;;
236
</pre>
237
238
We'll add a second function into the file *template.pkg*.
239
It's important to understand how this function works. Once the instance *bloc* which type is *ObjBloc* is created, we allocate the reflexive function using *_CBblocChangeValue* value.
240
Notice the '@' character just before the parameter, it represents the reflexive function that we want to call when the event is triggered. The character indicates that it's actually a pointer to the function. The parameter named *u0* is not used, so in our case we will set its value to *nil*.
241
<pre>
242
 /*! \brief Sample main function that show how to use a callback.
243
  *
244
  *  <b>Prototype:</b> fun [S I] I
245
  *
246
  *  \param S : bloc name
247
  *  \param I : bloc value
248
  *   
249
  *  \return I : 0
250
  **/
251
 fun objBlocTestEvent(nameValue, integerValue)=
252
   let _CRbloc _channel -> blocInstance in 
253
   {
254
     // Setting callback
255
     _CBblocChangeValue blocInstance @blocHasChanged nil; 
256
 
257
     // Set ObjBloc properties values
258
     _SETblocName blocInstance nameValue;
259
     _SETblocValue blocInstance integerValue;
260
 
261
     // Manually destroying blocInstance
262
     _DSbloc blocInstance;
263
   };
264
   0;;
265
</pre>
266
267
268
Now, we'll create another file into our user partition : *testObjBlocEvent.scol*. This Scol program will load the package *template.pkg* that we have just updated, and run the function *objBlocTestEvent* using 2 parameters :
269
* the name of the bloc (string value),
270
* its value (integer).
271
272
<pre>
273
 _load "template.pkg"
274
 objBlocTestEvent "newBloc" ff
275
</pre>
276
277
Finally, we can run the program *testObjBlocEvent.scol*. The log file should contain the following lines :
278
<pre>
279
 Loading C:\Users\Jeff\Documents\Scol Voyager\Partition_LocalUsr\template.pkg ...
280
 typechecking
281
 fun main : fun [] I
282
 fun objBlocTest : fun [S I] I
283
 fun blocHasChanged : fun [u0 u1 I S] I
284
 fun objBlocTestEvent : fun [S I] I
285
 Generating bytecodes for 'main'...
286
 3 bytes generated (for a total of 375 bytes)
287
 Generating bytecodes for 'objBlocTest'...
288
 79 bytes generated (for a total of 454 bytes)
289
 Generating bytecodes for 'blocHasChanged'...
290
 51 bytes generated (for a total of 505 bytes)
291
 Generating bytecodes for 'objBlocTestEvent'...
292
 28 bytes generated (for a total of 533 bytes)
293
 Loading complete
294
 
295
 
296
 > exec: objBlocTestEvent "newBloc" ff
297
 
298
 _CRbloc
299
 _CRbloc ...initialization successful
300
 _CRbloc ...MMmalloc successful
301
 _CRbloc ...object creation successful
302
 ok
303
 _CBblocChangeValue ...adding reflex
304
 _SETblocName
305
 Bloc name:  newBloc
306
 Bloc value: 0
307
 ok
308
 _SETblocValue
309
 Bloc name:  newBloc
310
 Bloc value: 255
311
 ok
312
 _DSbloc
313
 Bloc object destroyed.
314
 ok
315
</pre>