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