The two main macros for interfacing LispWorks with a foreign language are define-foreign-callable which defines Lisp functions that can be called from the foreign language, and define-foreign-function which defines a short linking function that can call functions in a foreign language.
In 1 Introduction to the FLI we defined a foreign function for calling the Win32 function SetCursorPos
. The code for this example is repeated here.
(fli:define-foreign-function (set-cursor-position "SetCursorPos") ((x :long) (y :long)) :result-type :boolean)
A FLI foreign function calling some C code. is an illustration of set-cursor-position
, represented by a square, calling the C code which constitutes SetCursorPos
.
The next diagram, C calling a callable function in Lisp., illustrates a callable function. Whereas a foreign function consists of a Lisp function name calling some code in C, a callable function consists of Lisp code, represented by an oval in the diagram, which can be called from C.
Callable functions are defined using fli:define-foreign-callable, which takes as its arguments, amongst other things, the name of the C function that will call Lisp, the arguments for the callable function, and a body of code which makes up the callable function.
To call a Lisp function from C or C++ you need to define it using fli:define-foreign-callable. Then call fli:make-pointer with the :symbol-name
argument and pass the result to C or C++ as a function pointer.
For the purpose of creating a self-contained illustration in Lisp, the following Lisp code defines a foreign callable function that takes the place of the Windows function SetCursorPos
.
(fli:define-foreign-callable ("SetCursorPos" :result-type :boolean) ((x :long) (y :long)) (capi:display-message "The cursor position can no longer be set"))
Supposing you had the above foreign callable defined in a real application, you would use:
(make-pointer :symbol-name "SetCursorPos")
to create a foreign pointer which you pass to foreign code so that it can call the Lisp definition of SetCursorPos
.
A FLI foreign function calling a callable function. illustrates what happens when set-cursor-position
is called. The foreign function set-cursor-position
(represented by the square) calls what it believes to be the Windows function SetCursorPos
, but the callable function (represented by the oval), also called SetCursorPos
, is called instead. It pops up a CAPI pane displaying the message "The cursor position can no longer be set".
For more information on calling foreign code see define-foreign-function.
For more information on defining foreign callable functions see 4.1.1 Strings and foreign callables and define-foreign-callable.
For information on how to create a LispWorks DLL, see "Creating a dynamic library" in the LispWorks® User Guide and Reference Manual.
For some complete examples of building a LispWorks DLL, then loading and calling it from foreign code, see "Delivering a dynamic library" in the Delivery User Guide.
To interface to a C function which takes a pointer to a string form and puts a string in the memory pointed to by result, declared like this:
void evalx(const char *form, char *result);
you would define in Lisp:
(fli:define-foreign-function evalx ((form (:reference-pass :ef-mb-string)) (:ignore (:reference-return (:ef-mb-string :limit 1000)))))
and call:
(evalx "(+ 2 3)") => "5"
Now suppose instead that you want your C program to call a similar routine in a LispWorks for Windows DLL named "evaluator", like this:
{ typedef void (_stdcall *evalx_func_type)(const char *form, char *result); HINSTANCE dll = LoadLibrary("evaluator"); evalx_func_type evalx = (evalx_func_type) GetProcAddress(dll, "evalx"); char result[1000]; evalx("(+ 2 3)", result); printf("%s\n", result); }
You would put this foreign callable in your DLL built with LispWorks:
(fli:define-foreign-callable ("evalx" :calling-convention :stdcall) ((form (:reference :ef-mb-string :lisp-to-foreign-p nil :foreign-to-lisp-p t)) (result (:reference (:ef-mb-string :limit 1000) :lisp-to-foreign-p t :foreign-to-lisp-p nil))) (multiple-value-bind (res err) (ignore-errors (read-from-string form)) (setq result (if (not (fixnump err)) (format nil "Error reading: ~a" err) (multiple-value-bind (res err) (ignore-errors (eval res)) (if (and (not res) err) (format nil "Error evaluating: ~a" err) (princ-to-string res)))))))
Note: you could use :reference-return and :reference-pass in the foreign callable definition, but we have shown :reference with explicit lisp-to-foreign-p and foreign-to-lisp-p arguments to emphasise the direction of each conversion.
Foreign Language Interface User Guide and Reference Manual - 01 Dec 2021 19:34:57