1 /* File mosml/src/mosmllib/test/callback/cside.c -- testing Callback.
2
3 How to access SML values from C, and how to create SML values to be
4 returned to SML. The C side of things. Updated 2000-06-16.
5 */
6
7 #include <ctype.h> /* For toupper */
8 #include <stdlib.h> /* For malloc */
9 #include <string.h> /* For strcpy */
10
11 /* Moscow ML specific includes: */
12
13 #include <mlvalues.h> /* For Val_unit, Long_val, String_val, ... */
14 #include <alloc.h> /* For copy_string, alloc_string, ... */
15 #include <memory.h> /* For Modify, Push_roots, Pop_roots */
16 #include <str.h> /* For string_length */
17 #include <callback.h>
18
19
20 #ifdef WIN32
21 #define EXTERNML __declspec(dllexport)
22 #else
23 #define EXTERNML
24 #endif
25
26
27 /* SML type: int -> int */
28
cfi(value v)29 value cfi(value v)
30 {
31 long i = Long_val(v);
32
33 return Val_long(i + 1);
34 }
35
36
37 /* SML type: unit -> unit */
38
cfu(value dummy)39 value cfu(value dummy)
40 {
41 return Val_unit;
42 }
43
44
45 /* SML type: char -> char */
46
cfc(value v)47 value cfc(value v)
48 {
49 char c = (char)Long_val(v);
50
51 return Val_long((long)(toupper(c)));
52 }
53
54
55 /* SML type: real -> real */
56
cfr(value v)57 value cfr(value v)
58 {
59 double d = Double_val(v);
60
61 return copy_double(2 * d);
62 }
63
64
65 /* SML type: string -> string */
66
cfs(value v)67 value cfs(value v)
68 {
69 char *oldp, *newp, *q;
70 int i, len;
71 oldp = String_val(v); /* Null-terminated heap-allocated string */
72 len = string_length(v); /* Much faster than strlen */
73
74 q = newp = malloc(len+1); /* malloc a C string */
75 strcpy(newp, oldp); /* and copy the given ML string to it */
76
77 while (*q) /* Modify the C string */
78 { *q = toupper(*q); q++; }
79
80 return copy_string(newp); /* Copy modified string to the ML heap */
81 }
82
83
84 /* SML type: bool -> bool */
85
cfb(value v)86 value cfb(value v)
87 {
88 int b = Bool_val(v);
89
90 return Val_bool(!b);
91 }
92
93
94 /* SML type: int -> char -> real -> string -> bool -> int */
95
cfcur(value vi,value vc,value vr,value vs,value vb)96 value cfcur(value vi, value vc, value vr, value vs, value vb)
97 {
98 long i = Long_val(vi);
99 char c = (char)Long_val(vc);
100 double d = Double_val(vr);
101 char *p = String_val(vs);
102 int b = Bool_val(vb);
103
104 return Val_long(i + c + (int)d + strlen(p) + b);
105 }
106
107
108 /* SML type: int * char * real -> int */
109 /* The components of a tuple v are Field(v, 0), Field(v, 1), ... */
110
cftup(value v)111 value cftup(value v)
112 {
113 long i = Long_val(Field(v, 0));
114 char c = (char)Long_val(Field(v, 1));
115 double d = Double_val(Field(v, 2));
116
117 return Val_long(i + c + (int)d);
118 }
119
120
121 /* SML type: { age : int, givenname : string, surname : string } -> bool */
122 /* A record is really a tuple, sorted lexicograhically on labels: */
123
cfrec(value v)124 value cfrec(value v)
125 {
126 long age = Long_val(Field(v, 0));
127 char *givennam = String_val(Field(v, 1));
128 char *surnam = String_val(Field(v, 2));
129
130 return Val_bool(age > 30 || strcmp(surnam, givennam) >= 0);
131 }
132
133
134 long treesum(value v); /* Forwards C declaration */
135
136 /* SML type: t -> int where
137 datatype t = Br of int * t * t | Brs of t list | Lf */
138
cfdat(value v)139 value cfdat(value v)
140 {
141 return Val_long(treesum(v));
142 }
143
144
145 /* Traversal of an SML list data structure (auxiliary function): */
146
listsum(value lst)147 long listsum(value lst)
148 {
149 #define isCons(x) (Tag_val(x) != 0)
150
151 long sum = 0;
152 while (isCons(lst)) /* While non-Nil */
153 {
154 value elem = Field(lst, 0); /* The list element = first arg of Cons */
155 sum += treesum(elem);
156 lst = Field(lst, 1); /* The list tail = second arg of Cons */
157 }
158 return sum;
159 }
160
161
162 /* Auxiliary function demonstrating traversal of SML tree data structure. */
163 /* Datatype constructors are sorted alphabetically (based on ASCII) and */
164 /* then numbered 0, 1, ...; the C code must use these numbers: */
165
treesum(value v)166 long treesum(value v)
167 {
168 long sum = 0;
169 int contag = Tag_val(v); /* 0 = Lf, 1 = Br, 2 = Brs */
170 switch (contag) {
171 case 2: /* Lf */
172 sum = 0; break;
173 case 0: /* Br(i, t1, t2) */
174 {
175 long i = Long_val(Field(v, 0));
176 value t1 = Field(v, 1); /* Left subtree */
177 value t2 = Field(v, 2); /* Right subtree */
178 sum = i + treesum(t1) + treesum(t2);
179 break;
180 }
181 case 1: /* Brs(tlist) */
182 {
183 value tlist = Field(v, 0); /* The list of subtrees */
184 sum = listsum(tlist);
185 break;
186 }
187 }
188 return sum;
189 }
190
191
192 /* SML type: (int -> string) -> int -> string */
193
cffun(value vf,value vi)194 value cffun(value vf, value vi)
195 {
196 int count = Val_long(vi);
197 int ok = 1;
198 value res;
199 int i;
200 Push_roots(r, 1);
201 r[0] = vf;
202 for (i=0; i<count; i++)
203 if (Double_val(callback(r[0], Val_long(i))) != i+7)
204 ok = 0;
205 /* NB: After a callback, the garbage collector may have run, so that
206 the pointer vf may have been invalidated. */
207 if (ok)
208 res = copy_string("Just right");
209 else
210 res = copy_string("Something is wrong");
211 Pop_roots();
212 return res;
213 }
214
215
216 /* SML type: int -> int * bool */
217
cfrtup(value vi)218 value cfrtup(value vi)
219 {
220 long i = Long_val(vi);
221 value tup = alloc_tuple(2); /* Allocate 2-element tuple in ML heap */
222 /* Must, in general, use modify to update because the GC is generational: */
223 modify(&Field(tup, 0), Val_long(i / 2));
224 modify(&Field(tup, 1), Val_bool(i % 2 == 1));
225 return tup;
226 }
227
228
229 /* SML type: int -> { half : int, odd : bool } */
230 /* A record is a sorted tuple, so this function is identical to that above */
231
cfrrec(value vi)232 value cfrrec(value vi)
233 {
234 long i = Long_val(vi);
235 value tup = alloc_tuple(2); /* Allocate 2-element tuple in ML heap */
236 /* Must, in general, use modify to update because the GC is generational: */
237 modify(&Field(tup, 0), Val_long(i / 2));
238 modify(&Field(tup, 1), Val_bool(i % 2 == 1));
239 return tup;
240 }
241
242
243 /* When allocating new values in the ML heap, special precautions must
244 be made. When the C function allocates in the ML heap, it may
245 cause the garbage collector to run, which may cause it to move
246 values from the young generation to the old one. To make sure that
247 the C function's copies of ML heap pointers are updated
248 accordingly, register them with Push_roots (and unregister them
249 with Pop_roots after use). This is necessary if the C function's
250 argument is any ML value other than int, char, or bool. */
251
252 /* SML type: string -> string -> string */
253 /* String concatenation (without error checking). The resulting string is
254 allocated in the ML heap, so pointers must be registered with Push_roots */
255
cfconcat(value s1,value s2)256 value cfconcat(value s1, value s2)
257 {
258 mlsize_t len1, len2;
259 value res;
260 /* The Push_roots macro introduces a declaration and thus can be
261 preceded only by declarations. If necessary, put it inside a new
262 block { ... }, in which Pop_roots must occur, too. */
263 Push_roots(r, 2);
264 r[0] = s1;
265 r[1] = s2;
266 len1 = string_length(s1);
267 len2 = string_length(s2);
268 /* Allocating the result in the heap may cause the GC to run, which
269 may move the strings given as arguments. But in this case, the
270 registered pointers r[0] and r[1] will be adjusted accordingly
271 (while s1 and s2 won't be) so it is safe to use them below: */
272 res = alloc_string(len1 + len2);
273 /* Byte(v, i) is the proper way to refer to byte i of value v: */
274 bcopy(&Byte(r[0],0), &Byte(res,0), len1);
275 bcopy(&Byte(r[1],0), &Byte(res,len1), len2);
276 /* Unregister pointers r[0] and r[1] to avoid space leaks: */
277 Pop_roots();
278 return res;
279 }
280
getting_notreg(value dummy)281 value getting_notreg(value dummy) {
282 valueptr mvp = get_valueptr("never registered");
283 return Val_bool(mvp == (valueptr)NULL);
284 }
285
getting_unreg(value dummy)286 value getting_unreg(value dummy) {
287 valueptr mvp = get_valueptr("unregistered");
288 return Val_bool(mvp == (valueptr)NULL);
289 }
290
using_notreg(value dummy)291 value using_notreg(value dummy) {
292 valueptr mvp = get_valueptr("never registered");
293 value val = get_value(mvp);
294 return Val_bool(mvp == (valueptr)NULL);
295 }
296
using_unreg(value dummy)297 value using_unreg(value dummy) {
298 valueptr temp1_ptr = get_valueptr("temp1");
299 value val;
300 if (temp1_ptr == (valueptr)NULL)
301 return Val_false;
302 unregistervalue("temp1");
303 val = get_value(temp1_ptr);
304 return Val_bool(val == (value)NULL);
305 }
306
callfunction(value startval)307 value callfunction(value startval) {
308 valueptr mlfunptr;
309 value d;
310 int i, max;
311 Push_roots(r, 1);
312 r[0] = startval;
313 mlfunptr = get_valueptr("extrafun");
314 max = Long_val(get_value(get_valueptr("steps")));
315 d = r[0];
316 Pop_roots();
317 for (i=0; i<max; i++)
318 d = callbackptr(mlfunptr, d);
319 return d;
320 }
321
initialize_callbacktest(value dummy)322 EXTERNML value initialize_callbacktest(value dummy) {
323 registercptr("call function", callfunction);
324
325 registercptr("getting_notreg", getting_notreg);
326 registercptr("getting_unreg", getting_unreg);
327 registercptr("using_notreg", using_notreg);
328 registercptr("using_unreg", using_unreg);
329
330 /* Order of definition, registration, and use should be different: */
331 registercptr("regcfdat", cfdat);
332 registercptr("regcfu", cfu);
333 registercptr("regcfi", cfi);
334 registercptr("regcfc", cfc);
335 registercptr("regcfr", cfr);
336 registercptr("regcfs", cfs);
337 registercptr("regcfb", cfb);
338 registercptr("regcfcur", cfcur);
339 registercptr("regcftup", cftup);
340 registercptr("regcfrec", cfrec);
341 registercptr("regcffun", cffun);
342 registercptr("regcfrtup", cfrtup);
343 registercptr("regcfrrec", cfrrec);
344 registercptr("regcfconcat", cfconcat);
345 return Val_unit;
346 }
347