Here is the C code for the example. On return, the argument string has been modified (the code assumes there is enough space after the string for the extra characters).
#include <stdio.h>
#include <string.h>
__declspec(dllexport) void __cdecl modify(char *string) {
char temp[256];
sprintf(temp, "'%s' modified in a C function", string);
strcpy(string, temp);
}
#include <stdio.h>
#include <string.h>
void modify(char *string) {
char temp[256];
sprintf(temp, "'%s' modified in a C function", string);
strcpy(string, temp);
}
Here are three approaches to calling
modify
from Lisp:
1. Use a fixed size buffer in define-foreign-function. This uses the :reference type, which automatically allocates a temporary foreign object, fills it with data converted from the Lisp object, passes a pointer to C and converts the data in the foreign object back into a new Lisp object on return. Note that the Lisp object passed to the function is not modified. This is the neatest way, provided you can bound the size of the result string at compile-time.
(fli:define-foreign-function (dff-modify "modify" :source)
((string (:reference (:ef-mb-string :limit 256))))
:calling-convention :cdecl)
(dff-modify "Lisp String")
=>
"'Lisp String' modified in a C function"
2. Use a fixed size buffer from with-dynamic-foreign-objects. In this case, we do most of the conversion explicitly and define the foreign function as taking a
:pointer
argument. This is a good approach if you don't know the maximum length when the function is defined, but will know it at compile-time for each call to the function.
(fli:define-foreign-function (wdfo-modify "modify" :source)
((string :pointer))
:calling-convention :cdecl)
(fli:with-dynamic-foreign-objects
((c-string (:ef-mb-string :limit 256)
:initial-element "Lisp String"))
(wdfo-modify c-string)
(fli:convert-from-foreign-string c-string))
=>
"'Lisp String' modified in a C function"
3. With a variable size buffer from allocate-dynamic-foreign-object. In this case, we do all of the conversion explicitly because we need to make an array of the right size, which is only known after the foreign string has been created (the extra 100 bytes are to allow for what the C function inserts into the string). Note that, in order to support arbitrary external formats, the code makes no assumptions about the length of the temporary array being the same as the length of the Lisp string: it does the conversion first using with-foreign-string, which works out the required number of bytes. The use of
with-dynamic-foreign-objects provides a dynamic scope for call to
allocate-dynamic-foreign-object - on exit, the foreign object will be freed automatically.
(fli:with-foreign-string (temp element-count byte-count)
"Lisp String"
(fli:with-dynamic-foreign-objects ()
(let ((c-string (fli:allocate-dynamic-foreign-object
:type '(:unsigned :byte)
:nelems (+ byte-count 100))))
(fli:replace-foreign-object c-string temp :nelems byte-count)
(wdfo-modify c-string)
(fli:convert-from-foreign-string c-string))))