emacs.d/clones/lispcookbook.github.io/cl-cookbook/ffi.html

225 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta name="generator" content=
"HTML Tidy for HTML5 for Linux version 5.2.0">
<title>Foreign Function Interfaces</title>
<meta charset="utf-8">
<meta name="description" content="A collection of examples of using Common Lisp">
<meta name="viewport" content=
"width=device-width, initial-scale=1">
<link rel="stylesheet" href=
"assets/style.css">
<script type="text/javascript" src=
"assets/highlight-lisp.js">
</script>
<script type="text/javascript" src=
"assets/jquery-3.2.1.min.js">
</script>
<script type="text/javascript" src=
"assets/jquery.toc/jquery.toc.min.js">
</script>
<script type="text/javascript" src=
"assets/toggle-toc.js">
</script>
<link rel="stylesheet" href=
"assets/github.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h1 id="title-xs"><a href="index.html">The Common Lisp Cookbook</a> &ndash; Foreign Function Interfaces</h1>
<div id="logo-container">
<a href="index.html">
<img id="logo" src="assets/cl-logo-blue.png"/>
</a>
<div id="searchform-container">
<form onsubmit="duckSearch()" action="javascript:void(0)">
<input id="searchField" type="text" value="" placeholder="Search...">
</form>
</div>
<div id="toc-container" class="toc-close">
<div id="toc-title">Table of Contents</div>
<ul id="toc" class="list-unstyled"></ul>
</div>
</div>
<div id="content-container">
<h1 id="title-non-xs"><a href="index.html">The Common Lisp Cookbook</a> &ndash; Foreign Function Interfaces</h1>
<!-- Announcement we can keep for 1 month or more. I remove it and re-add it from time to time. -->
<p class="announce">
📹 <a href="https://www.udemy.com/course/common-lisp-programming/?couponCode=6926D599AA-LISP4ALL">NEW! Learn Lisp in videos and support our contributors with this 40% discount.</a>
</p>
<p class="announce-neutral">
📕 <a href="index.html#download-in-epub">Get the EPUB and PDF</a>
</p>
<div id="content"
<p>The ANSI Common Lisp standard doesnt mention this topic. So almost everything that can be said here depends on your OS and your implementation.</p>
<p><a name="clisp-gethost"></a></p>
<h3 id="example-calling-gethostname-from-clisp">Example: Calling gethostname from CLISP</h3>
<p>Note: You should read the <a href="http://clisp.sourceforge.net/impnotes.html#dffi">relevant chapter</a> from the CLISP implementation notes before you proceed.</p>
<p><code>int gethostname(char *name, int len)</code> follows a typical pattern of C “out”-parameter convention - it expects a pointer to a buffer its going to fill. So you must view this parameter as either <code>:OUT</code> or <code>:IN-OUT</code>. Additionally, one must tell the function the size of the buffer. Here <code>len</code> is just an <code>:IN</code> parameter. Sometimes this will be an <code>:IN-OUT</code> parameter, returning the number of bytes actually filled in.</p>
<p>So <code>name</code> is actually a pointer to an array of up to <code>len</code> characters, regardless of what the poor “<code>char *</code>” C prototype says, to be used like a C string (0-termination). How many elements are in the array? Luckily, in our case, you can find it out without calculating the <code>sizeof()</code> a C structure. Its a hostname that will be returned. The Solaris 2.x manpage says “Host names are limited to MAXHOSTNAMELEN characters, currently 256.”</p>
<p>Also, in the present example, you can use allocation <code>:ALLOCA</code>, like youd do in C: stack-allocate a temporary. Why make things worse when using Lisp than when using C?</p>
<p>This yields the following useful signature for your foreign function:</p>
<pre><code class="language-lisp">(ffi:def-c-call-out gethostname
(:arguments (name (ffi:c-ptr (ffi:c-array-max ffi:char 256))
:out :alloca)
(len ffi:int))
;; (:return-type BOOLEAN) could have been used here
;; (Solaris says it's either 0 or -1).
(:return-type ffi:int))
(defun myhostname ()
(multiple-value-bind (success name)
;; :OUT or :IN-OUT parameters are returned via multiple values
(gethostname 256)
(if (zerop success)
(subseq name 0 (position #\null name))
(error ... ; errno may be set
...))))
(defvar hostname (myhostname))
</code></pre>
<p>Possibly <code>SUBSEQ</code> and <code>POSITION</code> are superfluous, thanks to <code>C-ARRAY-MAX</code> as opposed to <code>C-ARRAY</code>:</p>
<pre><code class="language-lisp">(defun myhostname ()
(multiple-value-bind (success name)
;; :out or :in-out parameters are returned via multiple values
(gethostname 256)
(if (zerop success) name
(error ... ; errno may be set
...))))
</code></pre>
<p><a name="alisp-gethost"></a></p>
<h3 id="example-calling-gethostname-from-allegro-cl">Example: Calling gethostname from Allegro CL</h3>
<p>This is how the same example above would be written in Allegro Common Lisp version 6 and above. ACL doesnt explicitly distinguish between <code>input</code> and <code>output</code> arguments. The way to declare an argument as <code>output</code> (i.e., modifiable by C) is to use an array, since arrays are passed by reference and C therefore receives a pointer to a memory location (which is what it expects). In this case things are made even easier by the fact that <code>gethostname()</code> expects an array of char, and a <code>SIMPLE-ARRAY</code> of <code>CHARACTER</code> represents essentially the same thing in Lisp. The foreign function definition is therefore the following:</p>
<pre><code class="language-lisp">(def-foreign-call (c-get-hostname "gethostname")
((name (* :char) (simple-array 'character (*)))
(len :int integer))
:returning :int)
</code></pre>
<p>Lets read this line by line: this form defines a Lisp function called <code>C-GET-HOSTNAME</code> that calls the C function <code>gethostname()</code>. It takes two arguments: the first one, called <code>NAME</code>, is a pointer to a char (<code>*char</code> in C), and a <code>SIMPLE-ARRAY</code> of characters in Lisp; the second one is called <code>LEN</code>, and is an integer. The function returns an integer value.</p>
<p>And now the Lisp side:</p>
<pre><code class="language-lisp">(defun get-hostname ()
(let* ((name (make-array 256 :element-type 'character))
(result (c-get-hostname name 256)))
(if (zerop result)
(let ((pos (position #\null name)))
(subseq name 0 pos))
(error "gethostname() failed."))))
</code></pre>
<p>This function creates the <code>NAME</code> array, calls <code>C-GET-HOSTNAME</code> to fill it and then checks the returned value. If the value is zero, then the call was successful, and we return the contents of <code>NAME</code> up to the first 0 character (the string terminator in C), otherwise we signal an error. Note that, unlike the previous example, we allocate the string in Lisp, and we rely on the Lisp garbage collector to get rid of it after the function terminates. Here is a usage example:</p>
<pre><code class="language-lisp">* (get-hostname)
"terminus"
</code></pre>
<p>Working with strings is, in general, easier than the previous example showed. Lets say you want to call <code>getenv()</code> from Lisp to access the value of an environment variable. <code>getenv()</code> takes a string argument (the variable name) and returns another string (the variable value). To be more precise, the argument is a <em>pointer</em> to a sequence of characters that should have been allocated by the caller, and the return value is a pointer to an already-existing sequence of chars (in the environment). Here is the definition of <code>C-GETENV</code>:</p>
<pre><code class="language-lisp">(def-foreign-call (c-getenv "getenv")
((var (* :char) string))
:returning :int
:strings-convert t)
</code></pre>
<p>The argument in this case is still a pointer to char in C, but we can declare it a <code>STRING</code> to Lisp. The return value is a pointer, so we declare it as integer. Finally, the <code>:STRINGS-CONVERT</code> keyword argument specifies that ACL should automatically translate the Lisp string passed as the first argument into a C string. Here is how its used:</p>
<pre><code class="language-lisp">* (c-getenv "SHELL")
-1073742215
</code></pre>
<p>If you are surprised by the return value, just remember that <code>C-GETENV</code> returns a pointer, and we must tell Lisp how to interpret the contents of the memory location pointed to by it. Since in this case we know that it will point to a C string, we can use the <code>FF:NATIVE-TO-STRING</code> function to convert it to a Lisp string:</p>
<pre><code class="language-lisp">* (native-to-string (c-getenv "SHELL"))
"/bin/tcsh"
9
9
</code></pre>
<p>(The second and third values are the number of characters and bytes copied, respectively). One caveat: if you ask for the value of a non-existent variable, <code>C-GETENV</code> will return 0, and <code>NATIVE-TO-STRING</code> will fail. So a safer example would be:</p>
<pre><code class="language-lisp">* (let ((ptr (c-getenv "NOSUCHVAR")))
(unless (zerop ptr)
(native-to-string ptr)))
NIL
</code></pre>
<p class="page-source">
Page source: <a href="https://github.com/LispCookbook/cl-cookbook/blob/master/ffi.md">ffi.md</a>
</p>
</div>
<script type="text/javascript">
// Don't write the TOC on the index.
if (window.location.pathname != "/cl-cookbook/") {
$("#toc").toc({
content: "#content", // will ignore the first h1 with the site+page title.
headings: "h1,h2,h3,h4"});
}
$("#two-cols + ul").css({
"column-count": "2",
});
$("#contributors + ul").css({
"column-count": "4",
});
</script>
<div>
<footer class="footer">
<hr/>
&copy; 2002&ndash;2021 the Common Lisp Cookbook Project
</footer>
</div>
<div id="toc-btn">T<br>O<br>C</div>
</div>
<script text="javascript">
HighlightLisp.highlight_auto({className: null});
</script>
<script type="text/javascript">
function duckSearch() {
var searchField = document.getElementById("searchField");
if (searchField && searchField.value) {
var query = escape("site:lispcookbook.github.io/cl-cookbook/ " + searchField.value);
window.location.href = "https://duckduckgo.com/?kj=b2&kf=-1&ko=1&q=" + query;
// https://duckduckgo.com/params
// kj=b2: blue header in results page
// kf=-1: no favicons
}
}
</script>
<script async defer data-domain="lispcookbook.github.io/cl-cookbook" src="https://plausible.io/js/plausible.js"></script>
</body>
</html>