Use another language to call a function: Difference between revisions

→‎{{header|TXR}}: Break into sections; add cptr-memcpy based solution.
(→‎{{header|TXR}}: Break into sections; add cptr-memcpy based solution.)
Line 1,067:
 
=={{header|TXR}}==
 
=== Using character array ===
 
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.
Line 1,121 ⟶ 1,123:
Note also how the data is prepared. As a special case, FFI creates a correspondence between the <code>char</code> array and a character string. The callback must mutate the character string to the desired value; FFI will then propagate the mutation to the original array. If the callback mistakenly performed <code>(set data s)</code>, it wouldn't work, because the original string object is untouched. Only the lexical <code>data</code> variable is replaced with a pointer <code>s</code>. The expression <code>(set [data :..:] s)</code> replaces a subrange of <code>data</code> with <code>s</code>, where the subrange is all of <code>data</code>.
 
Finally, note that TXR Lisp strings are Unicode, stored as arrays of wide characters (C type <code>wchar_t</code>). FFI is doing automatic conversion between that representation and UTF-8. That's a specialized behavior of the <code>(array ... char)></code> type. If UTF-8 encoding is undesirable, then the <code>bchar</code> type can be used (byte char). Then there is a one to one correspondence between the Unicode characters and array elements. However, out-of-range Unicode characters (values above U+007F) trigger an exception.
But our approach has a problem: it uses FFI in a way that relies on knowing the size of the C object, which is incorrect. The C buffer could be of any size; the only indicator we can trust is the run-time value we are given.
 
=== Using <code>carray</code> ===
 
ButOur ourabove approach has a problem: it uses FFI in a way that relies on knowing the size of the C object, which is incorrect. The C buffer could be of any size; the only indicator we can trust is the run-time value we are given.
 
To accurately deal with this kind of situation accurately, the lower level <code>carray</code> FFI type can be used:
Line 1,137 ⟶ 1,143:
(l (length s)))
(cond
((> (length s)l size) 0)
(t (each ((i (range* 0 l)))
(carray-refset buf i [s i]))
Line 1,158 ⟶ 1,164:
 
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.
 
=== Using <code>cptr</code> and <code>memcpy</code> ===
 
A more succinct approach is possible if we avail ourselves of the <code>memcpy</code> function via FFI. We can receive the data as an opaque foreign pointer represented by the <code>cptr</code> type. We can set up <code>memcpy</code> so that its destination argument and return value is a <code>cptr</code>, but the source argument is a string:
 
<lang txrlisp>(with-dyn-lib "./query.so"
(deffi query "query" void (closure)))
(with-dyn-lib nil
(deffi memcpy "memcpy" cptr (cptr str size-t)))
(deffi-cb query-cb int (cptr (ptr (array 1 size-t))))
(query (query-cb (lambda (buf sizeptr)
(symacrolet ((size [sizeptr 0]))
(let* ((s "Here am I")
(l (length s)))
(cond
((> l size) 0)
(t (memcpy buf s l)
(set size l))))))))</lang>
 
Here, the use of the <code>str</code> type in the <code>memcpy</code> interface means that FFI automatically produces a UTF-8 encoding of the string in a temporary buffer. The pointer to that temporary buffer is what is passed into <code>memcpy</code>. The temporary buffer is released after <code>memcpy</code> returns.
 
=={{header|zkl}}==
543

edits