205 lines
8.1 KiB
HTML
205 lines
8.1 KiB
HTML
<!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="icon" href=
|
||
"assets/cl-logo-blue.png"/>
|
||
<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> – 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> – 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 style="font-size: 120%" href="https://www.udemy.com/course/common-lisp-programming/?couponCode=LISPY-XMAS2023" title="This course is under a paywall on the Udemy platform. Several videos are freely available so you can judge before diving in. vindarel is (I am) the main contributor to this Cookbook."> Discover our contributor's Lisp course with this Christmas coupon.</a> -->
|
||
<!-- <strong> -->
|
||
<!-- Recently added: 18 videos on MACROS. -->
|
||
<!-- </strong> -->
|
||
<!-- <a style="font-size: 90%" href="https://github.com/vindarel/common-lisp-course-in-videos/">Learn more</a>. -->
|
||
<!-- </p> -->
|
||
|
||
<p class="announce">
|
||
📢 New videos: <a href="https://www.youtube.com/watch?v=h_noB1sI_e8">web dev demo part 1</a>, <a href="https://www.youtube.com/watch?v=xnwc7irnc8k">dynamic page with HTMX</a>, <a href="https://www.youtube.com/watch?v=Zpn86AQRVN8">Weblocks demo</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 doesn’t mention this topic. So almost everything that can be said here depends on your OS and your implementation. However these days, we can use the <a href="https://github.com/cffi/cffi">CFFI</a> library, a portable and easy-to-use C foreign function interface.</p>
|
||
|
||
<blockquote>
|
||
<p>CFFI, the Common Foreign Function Interface, purports to be a portable FFI for Common Lisp. It abstracts away the differences between the API of the native FFI’s of the various Common Lisp implementations.</p>
|
||
</blockquote>
|
||
|
||
<p>We’ll see an example right now.</p>
|
||
|
||
<h2 id="cffi-calling-a-c-function-from-the-mathh-header-file">CFFI: calling a C function from the <code>math.h</code> header file.</h2>
|
||
|
||
<p>Let’s use <code>defcfun</code> to interface with the foreign <a href="https://en.cppreference.com/w/c/numeric/math/ceil">ceil</a> C function from <code>math.h</code>.</p>
|
||
|
||
<p><a href="https://cffi.common-lisp.dev/manual/html_node/defcfun.html">defcfun</a> is a macro in the cffi library that generates a function with the name you give it.</p>
|
||
|
||
<pre><code class="language-lisp">CL-USER> (cffi:defcfund ("ceil" c-ceil) :double (number :double))
|
||
</code></pre>
|
||
|
||
<p>We say that the “ceil” C function will be called “c-ceil” on our Lisp side, it takes one argument that is a double float, and it returns a number that is also a double float.</p>
|
||
|
||
<p>Here is the above function macroexpanded with <code>macrostep-expand</code>:</p>
|
||
|
||
<pre><code class="language-lisp">(progn
|
||
nil
|
||
(defun c-ceil (number)
|
||
(let ((#:g312 number))
|
||
(cffi-sys:%foreign-funcall "ceil" (:double #:g312 :double) :convention
|
||
:cdecl :library :default))))
|
||
</code></pre>
|
||
|
||
<p>The reason we called it <code>c-ceil</code> and not <code>ceil</code> is only for the example, so we know this is a wrapper around C. You can name it “ceil”, since it doesn’t designate a built-in Common Lisp function or macro.</p>
|
||
|
||
<p>Now that we have a c-ceil function from <code>math.h</code>, let’s use it! We must give it double float.</p>
|
||
|
||
<pre><code class="language-lisp">CL-USER> (c-ceil 5.4d0)
|
||
6.0d0
|
||
</code></pre>
|
||
|
||
<p>As you can see, it works! The double gets rounded up to <code>6.0d0</code> as expected.</p>
|
||
|
||
<p>Let’s try another one! This time, we’ll use <a href="https://en.cppreference.com/w/c/numeric/math/floor">floor</a>, and we couldn’t name it “floor” because this Common Lisp function exists.</p>
|
||
|
||
<pre><code class="language-lisp">CL-USER> (cffi:defcfun ("floor" c-floor) :double (number :double))
|
||
C-FLOOR
|
||
CL-USER> (c-floor 5d0)
|
||
5.0d0
|
||
CL-USER> (c-floor 5.4d0)
|
||
5.0d0
|
||
</code></pre>
|
||
|
||
<p>Great!</p>
|
||
|
||
<p>One more, let’s try <code>sqrt</code> from math.h, still with double floats:</p>
|
||
|
||
<pre><code class="language-lisp">CL-USER> (cffi:defcfun ("sqrt" c-sqrt) :double (number :double))
|
||
C-SQRT
|
||
CL-USER> (c-sqrt 36.50d0)
|
||
6.041522986797286d0
|
||
</code></pre>
|
||
|
||
<p>We can do arithmetic with our new <code>c-sqrt</code>:</p>
|
||
|
||
<pre><code class="language-lisp">CL-USER> (+ 2 (c-sqrt 3d0))
|
||
3.732050807568877d0
|
||
</code></pre>
|
||
|
||
<p>We can even use our new shiny <code>c-sqrt</code> to map over a list of doubles and take the square root of all of them!</p>
|
||
|
||
<pre><code class="language-lisp">CL-USER> (mapcar #'c-sqrt '(3d0 4d0 5d0 6d0 7.5d0 12.75d0))
|
||
(1.7320508075688772d0 2.0d0 2.23606797749979d0 2.449489742783178d0
|
||
2.7386127875258306d0 3.570714214271425d0)
|
||
</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/>
|
||
© 2002–2023 the Common Lisp Cookbook Project
|
||
<div>
|
||
📹 Discover <a style="color: darkgrey; text-decoration: underline", href="https://www.udemy.com/course/common-lisp-programming/?referralCode=2F3D698BBC4326F94358">our contributor's Common Lisp video course on Udemy</a>
|
||
</div>
|
||
</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>
|