[18fe61]: ffi.xmlf Maximize Restore History

Download this file

ffi.xmlf    429 lines (372 with data), 17.4 kB

<?xml version="1.0"?><!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<book lang="en">
<chapter id="ext.ffi">
 <title>Foreign Function Interface</title>

 <section id="ext.ffi.what">
  <title>What is a FFI?</title>

  <para>A Foreign Function Interface, or &FFI; for short, is a means for a
  programming language to interface with libraries written in other programming
  languages, the <emphasis>foreign code</emphasis>. You will see this concept
  most often being used in interpreted environments, such as Python, Ruby or
  Lisp, where one wants to reuse the big number of libraries written in C
  and C++ for dealing with graphical interfaces, networking, filesystems,

  <para>A FFI is made of at least three components:</para>
    <term>Foreign objects management</term>
    <listitem><para>This is the data that the foreign code will use. A &FFI;
    needs to provide means to buid and manipulate foreign data, with atomatic
    conversions to and from lisp data types whenever possible, and it also has
    to deal with issues like garbage collection and
    <term>Foreign code loader</term>
    <listitem><para>To actually use a foreign routine, the code must reside in
    memory. The process of loading this code and finding out the addresses of
    the routines we want to use is normally done by an independent
    <term>Foreign function invocation</term>
    <listitem><para>This is the part of the &FFI; that deals with actually
    calling the foreign routines we want to use. For that one typically has to
    tell the &FFI; what are the arguments that these routines expect, what are
    the calling conventions and where are these routines to be

  <para>On top of these components sits a higher level interface written
  entirely in lisp, with which you will actually declare and use foreign
  variables, functions and libraries. In the following sections we describe
  both the details of the low-level components (<xref linkend="ext.ffi.dffi"/>,
  <xref linkend="ext.ffi.objects"/>), and of the higher level interface (<xref
  linkend="ext.ffi.uffi.and.cffi"/>). It is highly recommended that you read
  <emphasis>all</emphasis> sections.</para>

 <section id="ext.ffi.dffi">
  <title>Two kinds of FFI</title>

  <para>&ECL; allows for two different appraoches when building a &FFI;. Both
  approaches have a different implementation philosophy and affect the places
  where you can use the &FFI; and how.
    <term>Static &FFI;</term>
    <listitem><para>For every foreign function and variable you might need to
    use, a wrapper is automatically written in C with the help of <xref
    linkend="ref.c-inline"/>. These wrappers are compiled using an ordinary C
    compiler and linked against both the foreign libraries you want to use and
    against the &ECL; library. The result is a &FASL; file that can be loaded
    from &ECL; and where the wrappers appear as ordinary lisp functions and
    variables that the user may directly invoked.</para></listitem>
    <term>Dynamic &FFI;</term>
    <listitem><para>First of all, the foreign libraries are loaded in memory
    using the facilities of the operating system. Similar routines are used to
    find out and register the memory location of all the functions and
    variables we want to use. Finally, when actually accessing these functions,
    a little piece of assembly code does the job of translating the lisp data
    into foreign objects, storing the arguments in the stack and in CPU
    registers, calling the function and converting back the output of the
    function to lisp.</para></listitem>

  <figure float="1" id="fig.ffi">
   <title>FFI components</title>
     <imagedata align="center" fileref="figures/ffi.png"
		format="PNG" width="300px"/>
  </figure>As you see, the first approach uses rather portable technices based
  on a programming language (C, C++) which is strongly supported by the
  operating system. The conversion of data is performed calling routines in the
  &ECL; library and we need not care about the precise details (organizing the
  stack, CPU registers, etc) when calling a function: the compiler does this
  for us.</para>

  <para>On the other hand, the dynamic approach allows us to choose the
  libraries we load at any time, look for the functions and invoke them even
  from the toplevel, but it relies on unportable techniques and requires from
  us, the developers of &ECL;, to know very well both the assembly code of the
  machine &ECL; runs on and the calling conventions of that particular
  operating system.</para>

  <para>&ECL; currently supports the static method on all platforms, and the
  dynamical one a few of the most popular ones, shown in <xref
  linkend="table.dffi"/>. You can test if your copy of &ECL; was built with
  DFFI by inspecting whether the symbol <symbol>:DFFI</symbol> is present in
  the list from variable <symbol>*FEATURES*</symbol>.</para>

  <table id="table.dffi">
   <title>DFFI support</title>
   <tgroup cols="3">
      <entry>Operating systems</entry>
      <entry>Intel x86 32 bits</entry>
      <entry>Any with SysV ABI (Linux, BSD), Windows, OS X</entry>
      <entry>Intel x86 64 bits</entry>
      <entry>In progress</entry>
      <entry>SysV ABI</entry>
      <entry>PowerPC 32 bits</entry>
      <entry>In progress</entry>
      <entry>OS X</entry>

 <section id="ext.ffi.objects">
  <title>Foreign objects</title>

  <para>While the foreign function invocation protocols differ strongly between
  platforms and implementations, foreign objects are pretty easy to handle
  portably. For &ECL;, a foreign object is just a bunch of bytes stored in
  memory. The lisp object for a foreign object encapsulates several bits of
   <listitem><para>A list or a symbol specifying the C type of the
   <listitem><para>The pointer to the region of memory where data is
   <listitem><para>A flag determining whether &ECL; can automatically manage
   that piece of memory and deallocated when no longer in

  <para>A foreign object may contain many different kinds of data: integers,
  floating point numbers, C structures, unions, etc. The actual type of the
  object is stored in a list or a symbol which is understood by the higher
  level interface (<xref linkend="ext.ffi.uffi.and.cffi"/>).</para>

  <para>The most important component of the object is the memory region where
  data is stored. By default &ECL; assumes that the user will perform automatic
  managment of this memory, deleting the object when it is no longer
  needed. The first reason is that this block may have been allocated by a
  foreign routine using <function>malloc()</function>, or
  <function>mmap()</function>, or statically, by referring to a C constant. The
  second reason is that foreign functions may store references to this memory
  which &ECL; is not aware of and, in order to keep these references valid,
  &ECL; should not attempt to automatically destroy the object.</para>

  <para>In many cases, however, it is desirable to automatically destroy
  foreign objects once they have been used. The higher level interfaces &UFFI;
  and &CFFI; provide tools for doing this. For instance, in the following
  example adapted from the &UFFI; documentation, the string
  <varname>NAME</varname> is automatically deallocated</para>
<programlisting>(def-function "gethostname" 
  ((name (* :unsigned-char))
   (len :int))
  :returning :int)

(if (zerop (c-gethostname (uffi:char-array-to-pointer name) 256))
    (format t "Hostname: ~S" (ffi:convert-from-foreign-string name))
    (error "gethostname() failed."))

 <section id="ext.ffi.uffi.and.cffi">
  <title>Higher level interfaces</title>

  <para>Up to now we have only discussed vague ideas about how a &FFI; works,
  but you are probably more interested on how to actually code all these things
  in lisp. You have here three possibilities:</para>
    <para>&ECL; supplies a high level interface which is compatible with
    &UFFI;. Code designed for this library should run mostly unchanged with
    <para>The &CFFI; library features a mostly complete backend for &ECL;. This
    is however a work in progress, as the fact that &CFFI; allows for calling
    arbitrary functions without declaring them causes some troubles with
    <para>&ECL;'s own low level interface. Only to be used if &ECL; is your
    deployment platform. It features some powerful constructs that allow you to
    merge arbitrary C code with lisp (<xref linkend="ref.c-inline"/> and <xref

  <para>In the following two subsections we will discuss two practical examples
  of using the native &UFFI; and the &CFFI; library.</para>

  <section id="ext.ffi.uffi-example">
   <title>UFFI example</title>


  <section id="ext.ffi.cffi-example">
   <title>CFFI example</title>


 <section id="ext.ffi.dict">
  <title>FFI Reference</title>

<!-- ====================================================================== -->
<!-- FFI:CLINES                                                             -->
<!-- ====================================================================== -->

  <refentry id="ref.clines">
    <refpurpose>Insert C declarations and definitions</refpurpose>

    <title>Special form</title>
    <simplelist columns="2" type="horiz">
     <member>One or more strings with C definitions. Not evaluated</member>

     <member>No value.</member>


    <para>This special form inserts C code directly in the file that results
    from compiling lisp sources. It can only appear as a toplevel form and,
    contrary to <xref linkend="ref.c-inline"/>, it does not accept any input
    values nor returns any value.</para>

    <para>The main use of <function>FFI:CLINES</function> is to declare or
    define C variables and functions that are going to be used later in other
    &FFI; statements.</para>

    <para><function>FFI:CLINES</function> is a special form that can only be
    used in lisp compiled files as a toplevel form. Other uses will lead to an
    error being signaled, either at compilation time or when loading the


    <para>In this example the <function>FFI:CLINES</function> statement is
    required to get access to the C function <function>cos()</function></para>
<programlisting>(ffi:clines "#include &lt;math.h&gt;")
(defun cos (x)
  (ffi:c-inline (x) (:double) :double "cos(#0)" :on-liner t))</programlisting>


<!-- ====================================================================== -->
<!-- FFI:C-INLINE                                                           -->
<!-- ====================================================================== -->

  <refentry id="ref.c-inline">
    <refpurpose>Inline C code in a lisp form.</refpurpose>

    <title>Special form</title>
    <simplelist columns="2" type="horiz">
     <member>A lisp expression, evaluated.</member>

     <member>A valid <acronym>FFI</acronym> type.</member>

     <member>A valid <acronym>FFI</acronym> type or <code>(VALUES)</code>.</member>

     <member>A string with valid C code plus some valid escape forms.</member>

     <member>A boolean, defaults to <symbol>NIL</symbol>.</member>

     <member>A boolean, defaults to <symbol>T</symbol>.</member>

     <member>One or more lisp values.</member>


    <para>This is an special form which can be only used in compiled code and
    whose purpose is to execute some C code getting and returning values from
    and to the lisp environment.</para>

    <para>The first argument to <function>ffi:c-inline</function> is a list of
    lisp forms. These forms are going to be evaluated and their lisp values
    will be transformed to the corresponding C types denoted by

    <para>The input values are used to create a valid C expression using the
    template in <replaceable>C-code</replaceable>. This is a string of
    arbitrary size which mixes C expressions with two kind of
    escape forms.</para>

    <para>The first kind of escape form are made of a hash and a letter or a
    number, as in: <code>#0</code>, <code>#1</code>, ..., until
    <code>#z</code>. These codes are replaced by the corresponding input
    values. The second kind of escape form has the format <code>@(return
    <optional>n</optional>)</code>, it can be used as lvalue in a C expression
    and it is used to set the n-th output value of the
    <function>ffi:c-inline</function> form.</para>

    <para>When the parameter <replaceable>one-liner</replaceable> is true, then
    the C template must be a simple C statement that outputs a value. In this
    case the use of <code>@(return)</code> is not allowed. When the parameter
    <replaceable>one-liner</replaceable> is false, then the C template may be a
    more complicated block form, with braces, conditionals, loops and spanning
    multiple lines. In this case the output of the form can only be set using

    <para>Note that the conversion between lisp arguments and
    <acronym>FFI</acronym> types is automatic. Note also that
    <function>et:c-inline</function> cannot be used in interpreted or
    bytecompiled code!</para>


    <para>The following example implements the transcendental function
    <function>SIN</function> using the C equivalent</para>
    <programlisting>(ffi:c-lines "#include &lt;math.h&gt;")
(defun mysin (x)
  (ffi:c-inline (x) (:double) :double "sin(#0)" :one-liner t :side-effects nil))</programlisting>

    <para>This function can also be implemented using the
    <code>@(return)</code> form as follows:</para>
    <programlisting>(defun mysin (x)
  (ffi:c-inline (x) (:double) :double "@(return)=sin(#0);" :side-effects nil))</programlisting>

    <para>The following example is slightly more complicated as it involves
    loops and two output values:</para>
    <programlisting>(defun sample (x)
  (ffi:c-inline (n1 n2) (:int :int) (values :int :int) "{
    int n1 = #0, n2 = #1, out1 = 0, out2 = 1;
    while (n1 &lt;= n2) {
      out1 += n1;
      out2 *= n1;
    @(return 0)= out1;
    @(return 1)= out2;
   :side-effects nil))</programlisting>
 <!-- Keep this comment at the end of the file
      Local variables:
      mode: nxml
      sgml-parent-document: "ecl.xml"
      sgml-indent-step: 1
      nxml-child-indent: 1
      nxml-outline-child-indent: 1
      fill-column: 79