Use another language to call a function: Difference between revisions

→‎{{header|TXR}}: New section.
(→‎{{header|TXR}}: New section.)
(→‎{{header|TXR}}: New section.)
Line 1,067:
 
=={{header|TXR}}==
 
This is really two tasks: how to accept foreign callbacks, and how to link code to a C program which controls the <code>main</code> startup function.
 
The TXR run-time is not available as a library that can be linked to a C program. Instead, we can put the C driver into a small library and call out to it from TXR, then accept its callback. Here is that library:
 
<lang c>#include <stdio.h>
 
int query(int (*callback)(char *, size_t *))
{
char buffer[1024];
size_t size = sizeof buffer;
 
if (callback(buffer, &size) == 0) {
puts("query: callback failed");
} else {
char *ptr = buffer;
 
while (size-- > 0)
putchar (*ptr++);
putchar('\n');
}
}</lang>
 
Here are the build steps to produce a `query.so` object from it on GNU/Linux:
 
<lang shell>gcc -g -fPIC query.c -c
gcc -g --shared query.c -o query.c</lang>
 
Now an interactive TXR session.
 
Callbacks are modeled as "FFI closures". The macro <code>deffi-cb</code> defines a function which itself isn't a callback, but is rather a combinator which converts a Lisp function into a FFI callback.
 
<lang txrlisp>(with-dyn-lib "./query.so"
(deffi query "query" void (closure)))
 
(deffi-cb query-cb int ((ptr (array 1024 char)) (ptr size-t)))
 
(query (query-cb (lambda (data size)
(let ((s "Here am I"))
(cond
((> (length s) size) 0)
(t (set [data :..:] s)
(set size (length s))))))))</lang>
 
{{run}}
 
<pre>$ txr query.tl
Here am I</pre>
 
If the callback throws an exception or performs any other non-local return, it will return a default return value of all zero bits in the given return type. This value can be specified, but the zero default suits our particular situation:
 
<pre>$ txr
This is the TXR Lisp interactive listener of TXR 177.
Use the :quit command or type Ctrl-D on empty line to exit.
1> (with-dyn-lib "./query.so" (deffi query "query" void (closure)))
#:lib-0177
2> (deffi-cb query-cb int ((ptr (array 1024 char)) (ptr size-t)))
query-cb
3> (query (query-cb (lambda (x y) (error "oops"))))
query: callback failed
** oops
** during evaluation at expr-3:1 of form (error "oops")
4></pre>
 
Here we can see that when the callback throws the <code>error</code> exception, the C code prints <code>query: callback failed</code>, due to receiving the default abort return value of zero. Then, the exception continues up to the interactive prompt.
 
=={{header|zkl}}==
To make this as simple as possible, the [zkl] query program sets a variable and main.c runs query.zkl and extracts the variable. A more realistic scenario (which several of the extension libraries utilize) is to compile the zkl code, wad it into C code (a byte stream of the compiled code) and link that with main. Not hard but messy (the source of a suitable extension gives you something to copy). Also, this solution uses the shared library version of zkl (you could use the all in one version but you would go about it in a [slightly] different way).
543

edits