[Alice]

HowToInterface

The following 313 words could not be found in the dictionary of 635 words (including 635 LocalSpellingWords) and are highlighted below:

above   abstract   add   added   Addr   against   Alice   alice   alicetool   all   All   always   am   aml   an   and   anyway   argument   arguments   asig   assume   at   be   because   before   Believe   best   Big   big   binding   body   bootstrap   build   built   but   by   called   can   cannot   cases   cc   Cell   character   Command   compile   compiled   component   Component   Components   components   Config   convention   creates   current   currently   Debug   Debugger   declarations   declare   declared   Desc   describes   distribution   div   Dll   dll   don   done   double   dynamic   E0   elements   En   entry   epoch   Everything   example   examples   except   extend   few   file   files   final   for   foreign   Foreign   forget   from   ftime   fun   function   functional   functions   further   Further   give   Given   go   had   hardwire   How   how   If   if   implement   implementation   implemented   implementing   implements   implies   import   importantly   imported   in   In   include   inside   installed   instance   Instead   Int   int   integers   interactive   interested   interface   internal   into   itself   language   Large   Learning   less   Let   lib   libraries   library   libsystem   like   Line   link   look   Machine   macro   macros   Main   Makefile   Makefiles   me   messages   might   milliseconds   Milliseconds   millitm   missing   most   mpz   ms   mul   multiply   must   name   Name   named   naming   native   Native   need   never   New   new   note   Note   now   number   obtains   of   one   option   or   other   our   overview   packages   page   particular   parts   path   polymorphic   probably   procedure   provides   Rand   rare   record   Record   Reflect   register   registered   rely   remains   Remote   representation   res   restrict   return   returns   Rough   routines   S0   safely   same   seam   seconds   Seconds   Separate   several   should   sig   Signal   Signature   signature   simple   since   single   So   so   Socket   source   specifications   sqlite   static   still   straightforward   strange   struct   structure   Such   such   suitable   supported   system   table   tb   That   that   them   then   There   there   these   this   This   those   though   Time   time   timeb   tm   to   To   Together   toplevel   trivial   tructure   tuple   type   types   typing   ui   underscore   undocumented   unit   Unix   unless   unsafe   Unsafe   usable   use   using   usual   utility   val   valid   Value   value   variable   Virtual   vm   want   way   we   We   well   were   where   which   with   word   work   works   would   wrapper   wraps   write   writing   written   x0   yet   You   you   your  

Clear message

How to interface C from Alice

This page describes how to interface to foreign C code from Alice, by implementing so-called native components, i.e. components written in C/C++ that are usable as Alice components.

Rough overview

To write a component X which wraps C code you should write four files:

X-sig.aml

Signature of the final component.

X.aml

Everything that can be implemented in Alice ML should go here.

NativeX.asig

Signature of those parts which are not implemented in Alice ML, but in C++.

NativeX.cc

C++ implementation of those native functions (on the SEAM Virtual Machine).

Learning by example

Let's assume that we want to write a new component Time, which most importantly should have a function now : unit -> time. First of all we would write it's signature TIME-sig.aml:

signature TIME =
sig
    type time

    val fromSeconds : LargeInt.int -> time
    val fromMilliseconds : LargeInt.int -> time

    val toSeconds : time -> LargeInt.int
    val toMilliseconds : time -> LargeInt.int

    val now : unit -> time
end

A suitable internal representation are big integers LargeInt.int. We can implement all of the routines above in Alice ML, except for Time.now, which is a native function declared in NativeTime.asig:

signature NATIVE_TIME_COMPONENT =
sig
    structure NativeTime :
    sig
        (* return current time in milliseconds since the epoch *)
        val now : unit -> LargeInt.int
    end
end

Please note that the signature should never have any type specifications! Alice ML implements dynamic typing (for packages), which implies that all type declarations have a dynamic representation. Believe me, you don't want to implement any dynamic type representation in C code.

So if you have abstract types, you cannot use them in the native signature. Instead, you give more polymorphic types there (it's unsafe anyway), and rely on the wrapper component to safely restrict the types.


Given such a native procedure, writing Time.aml is straightforward: alice/lib/system/Time.aml:


import signature TIME       from "TIME-sig"
import structure NativeTime from "NativeTime"

tructure Time : TIME =
struct
  type time = LargeInt.int

  fun fromMilliseconds ms = ms
  
  fun toMilliseconds t = t

  fun fromSeconds s = ms * 1000

  fun toSeconds t = t div 1000

  val now = NativeTime.now 
end

This component can be compiled as usual, but is not yet functional, because the imported NativeTime is still missing.

Native Components in SEAM

SEAM is written in C++, so that is the language you have to use for the native component, NativeTime.cc:

/* you always have to include this: */
#include "alice/Authoring.hh"

/* in our example we use ftime to implement Time.now, so we need
   to include: */
#include <sys/timeb.h>

/* naming convention:  Separate ComponentName and procedure name
   by a single underscore character '_'.
 
   Note that there are several DEFINEn macros, where n is the
   number of arguments. That is number of elements of the 
   argument tuple. 

   If we had arguments, then these would be named x0...x(n-1) inside
   the DEFINEn macro. For instance, by writing
     DECLARE_INT(i, x0);
   inside the body we declare a C int variable `i' that obtains
   the ML int value from the first argument.
 */

DEFINE0(NativeTime_now) {
  struct timeb tb;
  ftime (&tb);
  BigInt *res = BigInt::new ((double)tb.time);
  /* Note: BigInt are implemented using the GMP.
     BigInt::big returns GMP value.
   */
  /* multiply number of seconds by 1000 */
  mpz_mul_ui (res->big (), res->big (), 1000UL);
  /* add the milliseconds */
  mpz_add_ui (res->big (), res->big (), tm.millitm);

  /* return the value, never forget to return a valid alice value.
     If you implement a function which returns unit use
     the RETURN_UNIT macro.
   */
  RETURN_INTINF(res);
} END /* <- do not forget this END, unless you are interested in 
            strange error messages */


/* You must also write a function which creates the component
   itself.  This should always look more or less like this:
 */

AliceDll word NativeTime() {
  Record *record = Record::New (1);
  INIT_STRUCTURE(record, "NativeTime", "now", NativeTime_now, 1);
  RETURN_STRUCTURE("NativeTime$", record);
}

We have to compile this file into a dynamic library (DLL). Together with the .asig file, such a DLL can be imported by an Alice ML component as if it were an Alice component as well (this is not supported in the interactive toplevel, though).

You first have to compile the C++ source file. To build the DLL, it's probably best to use alicetool (which is installed with Alice, but currently undocumented):

  alicetool link NativeTime.o -o NativeTime.dll

If your DLL has to link against other dynamic libraries, you have to add them before the -o option.

That's it, we are done. You can now use your new Time component.

Further examples

Please have a look at the libraries in the Alice CVS for further examples. In particular, lib/sqlite provides a less trivial, but still simple example of a foreign binding.

New built-in components

In rare cases you might need to extend the Alice VM itself with so-called built-in components. This works in the same way as before, except that you do not import the native component, but hardwire it into the VM's table of so-called 'unsafe' components.

Such components have to be registered in alice/vm-seam/AliceMain.cc. There is a table called nativeComponents to which one entry must be added:

...
static NativeComponent nativeComponents[] = {
  {"lib/system/UnsafeConfig",       UnsafeConfig},
  {"lib/system/UnsafeIODesc",       UnsafeIODesc},
  {"lib/system/UnsafeOS",           UnsafeOS},
  {"lib/system/UnsafeUnix",         UnsafeUnix},
  {"lib/system/UnsafeCommandLine",  UnsafeCommandLine},
  {"lib/system/UnsafeComponent",    UnsafeComponent},
  {"lib/system/UnsafeDebug",        UnsafeDebug},
#if DEBUGGER
  {"lib/system/UnsafeDebugger",     UnsafeDebugger},
#endif
  {"lib/system/UnsafeForeign",      UnsafeForeign},
  {"lib/system/UnsafeSignal",       UnsafeSignal},
  {"lib/system/UnsafeSocket",       UnsafeSocket},
  {"lib/system/UnsafeRand",         UnsafeRand},
  {"lib/system/UnsafeValue",        UnsafeValue},
  {"lib/system/UnsafeReflect",      UnsafeReflect},
  {"lib/system/UnsafeTime",         UnsafeTime},        /* <-- like this */
  {"lib/utility/UnsafeCell",        UnsafeCell},
  {"lib/utility/UnsafeAddr",        UnsafeAddr},
  {"lib/distribution/UnsafeRemote", UnsafeRemote},
  {NULL, NULL}
};
...

All that remains is to register our work in a few Makefiles:

alice/vm-seam/Makefile.bootstrap.in::

alice/vm-seam/alice/lib/system/Makefile.am::