Project

General

Profile

Scol reflexive functions » History » Version 1

ModularMix, 10/18/2011 03:48 PM

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
 int getBlocNewData(mmachine m, HWND h, unsigned msg, UINT id, LONG param, int *ret)
47
 {
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
     OBJdefEvent(BLOC_NEW_DATA_CB, (int (__cdecl *)(struct Mmachine *, int, unsigned int, int, int, int *))getBlocNewData);
101
 
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
 // send the change data callback message to scol, use PostMessage instead if you need to poll the message
112
 SendMessage( HScol, BLOC_NEW_DATA_CB,(int)bloc,(LPARAM)NULL);
113
</pre>
114
115
116
h2. Registration of a new reflexive function
117
118
In order to be able to call a reflexive function of a *Bloc* object, we first have to associate it to this instance.
119
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.
120
<pre>
121
 /*! @ingroup group1
122
 * \brief _CBblocChangeValue : This function sets the reflexive function to be executed when Bloc value change event happens
123
 *
124
 * <b>Prototype:</b> fun [BlocObj fun [ObjBloc u0 I S] u1 u0] BlocObj
125
 *
126
 * \param BlocObj : Bloc Object whose value has changed
127
 * \param fun [ObjBloc u0 I S] u1 : The reflexive function to call when the event occurs.
128
 * - I : int value of the object
129
 * - S   : name value of the object
130
 * \param u0 : User parameter
131
 *
132
 * \return BlocObj : The Bloc object whose value has changed
133
 */
134
 int _CBblocChangeValue(mmachine m)
135
 {
136
     // Add a reflex
137
     MMechostr(MSKDEBUG, "_CBblocChangeValue ...adding reflex\n");
138
     return OBJaddreflex(m, OBJBLOCSCOL, SCOL_BLOC_NEW_DATA_CB);
139
 }
140
</pre>
141
142
143
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.
144
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*.
145
Note that the prototype of the reflexive function is the second parameter.
146
<pre>
147
 //! Nb of Scol functions or types
148
 #define NbTplPKG	9
149
 
150
 /*!
151
  * Scol function names
152
  **/
153
 char* TplName[NbTplPKG] =
154
 {
155
     "_HelloWorld",
156
     "ObjBloc",
157
     "_CRbloc",
158
     "_DSbloc",
159
     "_GETblocValue",
160
     "_GETblocName",
161
     "_SETblocName",
162
     "_SETblocValue",
163
     "_CBblocChangeValue"
164
 };
165
 
166
 /*!
167
  * Pointers to C functions that manipulate the VM for each scol function previously defined
168
  **/
169
 int (*TplFunc[NbTplPKG])(mmachine m)=
170
 {
171
     _HelloWorld,
172
     NULL,
173
     _CRbloc,
174
     _DSbloc,
175
     _GETblocValue,
176
     _GETblocName,
177
     _SETblocName,
178
     _SETblocValue,
179
     _CBblocChangeValue
180
 };
181
 
182
 /*!
183
  * Nb of arguments of each scol function
184
  **/
185
 int TplNArg[NbTplPKG]=
186
 {
187
     0,
188
     TYPTYPE,
189
     1,
190
     1,
191
     1,
192
     1,
193
     2,
194
     2,
195
     3
196
 };
197
 
198
 /*!
199
  * Prototypes of the scol functions
200
  **/
201
 char* TplType[NbTplPKG]=
202
 {
203
     "fun [] I",                                        // _HelloWorld
204
     NULL,
205
     "fun [Chn] ObjBloc",				// _CRbloc
206
     "fun [ObjBloc] I",					// _DSbloc
207
     "fun [ObjBloc] I",                                 // _GETblocValue
208
     "fun [ObjBloc] S",                                 // _GETblocName
209
     "fun [ObjBloc S] I",                               // _SETblocName
210
     "fun [ObjBloc I] I",                               // _SETblocValue
211
     "fun [ObjBloc fun [ObjBloc u0 I S] u1 u0] ObjBloc" //_CBblocChangeValue
212
 };
213
</pre>
214
215
216
h2. Use of a reflexive function in Scol
217
218
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 :
219
<pre>
220
 /*! \brief Callback that show values of an ObjBloc whenever a property he owned is changed.
221
  *
222
  *  Values are save to console log.
223
  *
224
  *  <b>Prototype:</b> fun [u0 u1 u1 u2] I
225
  *
226
  *  \param u0: not used, but the bloc instance where a value has changed is passed.
227
  *  \param u1: not used
228
  *  \param I : bloc value
229
  *  \param S : bloc name
230
  *   
231
  *  \return I : 0
232
  **/
233
 fun blocHasChanged(blocInstance, userParam, newValue, newName)=
234
   // Log new values
235
   _fooS strcatn "Bloc name:  "::newName::"\nBloc value: "::(itoa newValue)::nil;
236
   0;;
237
</pre>
238
239
We'll add a second function into the file *template.pkg*.
240
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.
241
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*.
242
<pre>
243
 /*! \brief Sample main function that show how to use a callback.
244
  *
245
  *  <b>Prototype:</b> fun [S I] I
246
  *
247
  *  \param S : bloc name
248
  *  \param I : bloc value
249
  *   
250
  *  \return I : 0
251
  **/
252
 fun objBlocTestEvent(nameValue, integerValue)=
253
   let _CRbloc _channel -> blocInstance in 
254
   {
255
     // Setting callback
256
     _CBblocChangeValue blocInstance @blocHasChanged nil; 
257
 
258
     // Set ObjBloc properties values
259
     _SETblocName blocInstance nameValue;
260
     _SETblocValue blocInstance integerValue;
261
 
262
     // Manually destroying blocInstance
263
     _DSbloc blocInstance;
264
   };
265
   0;;
266
</pre>
267
268
269
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 :
270
* the name of the bloc (string value),
271
* its value (integer).
272
273
<pre>
274
 _load "template.pkg"
275
 objBlocTestEvent "newBloc" ff
276
</pre>
277
278
Finally, we can run the program *testObjBlocEvent.scol*. The log file should contain the following lines :
279
<pre>
280
 Loading C:\Users\Jeff\Documents\Scol Voyager\Partition_LocalUsr\template.pkg ...
281
 typechecking
282
 fun main : fun [] I
283
 fun objBlocTest : fun [S I] I
284
 fun blocHasChanged : fun [u0 u1 I S] I
285
 fun objBlocTestEvent : fun [S I] I
286
 Generating bytecodes for 'main'...
287
 3 bytes generated (for a total of 375 bytes)
288
 Generating bytecodes for 'objBlocTest'...
289
 79 bytes generated (for a total of 454 bytes)
290
 Generating bytecodes for 'blocHasChanged'...
291
 51 bytes generated (for a total of 505 bytes)
292
 Generating bytecodes for 'objBlocTestEvent'...
293
 28 bytes generated (for a total of 533 bytes)
294
 Loading complete
295
 
296
 
297
 > exec: objBlocTestEvent "newBloc" ff
298
 
299
 _CRbloc
300
 _CRbloc ...initialization successful
301
 _CRbloc ...MMmalloc successful
302
 _CRbloc ...object creation successful
303
 ok
304
 _CBblocChangeValue ...adding reflex
305
 _SETblocName
306
 Bloc name:  newBloc
307
 Bloc value: 0
308
 ok
309
 _SETblocValue
310
 Bloc name:  newBloc
311
 Bloc value: 255
312
 ok
313
 _DSbloc
314
 Bloc object destroyed.
315
 ok
316
</pre>