<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.20">
<title>CallingFromSMLToCFunctionPointer</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<link rel="stylesheet" href="./asciidoctor.css">
<link rel="stylesheet" href="./mlton.css">

</head>
<body class="article">
<div id="mlton-header">
<div id="mlton-header-text">
<h2>
<a href="./Home">
MLton
20241230+git20251029+dfsg-ok1
</a>
</h2>
</div>
</div>
<div id="header">
<h1>CallingFromSMLToCFunctionPointer</h1>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Just as MLton can <a href="CallingFromSMLToC">directly call C functions</a>, it
is possible to make indirect function calls; that is, function calls
through a function pointer.  MLton extends the syntax of SML to allow
expressions like the following:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>_import * : MLton.Pointer.t -&gt; real * char -&gt; int;</pre>
</div>
</div>
<div class="paragraph">
<p>This expression denotes a function of type</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sml">MLton.Pointer.t -&gt; real * char -&gt; int</code></pre>
</div>
</div>
<div class="paragraph">
<p>whose behavior is implemented by calling the C function at the address
denoted by the <code>MLton.Pointer.t</code> argument, and supplying the C
function two arguments, a <code>double</code> and an <code>int</code>.  The C function
pointer may be obtained, for example, by the dynamic linking loader
(<code>dlopen</code>, <code>dlsym</code>, &#8230;&#8203;).</p>
</div>
<div class="paragraph">
<p>The general form of an indirect <code>_import</code> expression is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>_import * attr... : cPtrTy -&gt; cFuncTy;</pre>
</div>
</div>
<div class="paragraph">
<p>The type and the semicolon are not optional.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_example">Example</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This example uses <code>dlopen</code> and friends (imported using normal
<code>_import</code>) to dynamically load the math library (<code>libm</code>) and call the
<code>cos</code> function. Suppose <code>iimport.sml</code> contains the following.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="sml">signature DYN_LINK =
   sig
      type hndl
      type mode
      type fptr

      val dlopen : string * mode -&gt; hndl
      val dlsym : hndl * string -&gt; fptr
      val dlclose : hndl -&gt; unit

      val RTLD_LAZY : mode
      val RTLD_NOW : mode
   end

structure DynLink :&gt; DYN_LINK =
   struct
      type hndl = MLton.Pointer.t
      type mode = Word32.word
      type fptr = MLton.Pointer.t

      (* These symbols come from a system libray, so the default import scope
       * of external is correct.
       *)
      val dlopen =
         _import "dlopen" : string * mode -&gt; hndl;
      val dlerror =
         _import "dlerror": unit -&gt; MLton.Pointer.t;
      val dlsym =
         _import "dlsym" : hndl * string -&gt; fptr;
      val dlclose =
         _import "dlclose" : hndl -&gt; Int32.int;

      val RTLD_LAZY = 0wx00001 (* Lazy function call binding.  *)
      val RTLD_NOW  = 0wx00002 (* Immediate function call binding.  *)

      val dlerror = fn () =&gt;
         let
            val addr = dlerror ()
         in
            if addr = MLton.Pointer.null
               then NONE
               else let
                       fun loop (index, cs) =
                          let
                             val w = MLton.Pointer.getWord8 (addr, index)
                             val c = Byte.byteToChar w
                          in
                             if c = #"\000"
                                then SOME (implode (rev cs))
                                else loop (index + 1, c::cs)
                          end
                    in
                       loop (0, [])
                    end
         end

      val dlopen = fn (filename, mode) =&gt;
         let
            val filename = filename ^ "\000"
            val hndl = dlopen (filename, mode)
         in
            if hndl = MLton.Pointer.null
               then raise Fail (case dlerror () of
                                   NONE =&gt; "???"
                                 | SOME s =&gt; s)
               else hndl
         end

      val dlsym = fn (hndl, symbol) =&gt;
         let
            val symbol = symbol ^ "\000"
            val fptr = dlsym (hndl, symbol)
         in
            case dlerror () of
               NONE =&gt; fptr
             | SOME s =&gt; raise Fail s
         end

      val dlclose = fn hndl =&gt;
         if MLton.Platform.OS.host = MLton.Platform.OS.Darwin
            then ()  (* Darwin reports the following error message if you
                      * try to close a dynamic library.
                      *   "dynamic libraries cannot be closed"
                      * So, we disable dlclose on Darwin.
                      *)
         else
            let
               val res = dlclose hndl
            in
               if res = 0
                  then ()
               else raise Fail (case dlerror () of
                                   NONE =&gt; "???"
                                 | SOME s =&gt; s)
            end
   end

val dll =
   let
      open MLton.Platform.OS
   in
      case host of
         Cygwin =&gt; "cygwin1.dll"
       | Darwin =&gt; "libm.dylib"
       | _ =&gt; "libm.so"
   end

val hndl = DynLink.dlopen (dll, DynLink.RTLD_LAZY)

local
   val double_to_double =
      _import * : DynLink.fptr -&gt; real -&gt; real;
   val cos_fptr = DynLink.dlsym (hndl, "cos")
in
   val cos = double_to_double cos_fptr
end

val _ = print (concat ["    Math.cos(2.0) = ", Real.toString (Math.cos 2.0), "\n",
                       "libm.so::cos(2.0) = ", Real.toString (cos 2.0), "\n"])

val _ = DynLink.dlclose hndl</code></pre>
</div>
</div>
<div class="paragraph">
<p>Compile and run <code>iimport.sml</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre>% mlton -default-ann 'allowFFI true'    \
        -target-link-opt linux -ldl     \
        -target-link-opt solaris -ldl   \
         iimport.sml
% iimport
    Math.cos(2.0) = ~0.416146836547
libm.so::cos(2.0) = ~0.416146836547</pre>
</div>
</div>
<div class="paragraph">
<p>This example also shows the <code>-target-link-opt</code> option, which uses the
switch when linking only when on the specified platform.  Compile with
<code>-verbose 1</code> to see in more detail what&#8217;s being passed to <code>gcc</code>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_download">Download</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p><a href="https://raw.github.com/MLton/mlton/master/doc/examples/ffi/iimport.sml"><code>iimport.sml</code></a></p>
</li>
</ul>
</div>
</div>
</div>
</div>
</body>
</html>