16 SWIG and Ocaml

This chapter describes SWIG's support of Ocaml. Ocaml is a relatively recent addition to the ML family, and is a recent addition to SWIG. It's the second compiled, typed language to be added. Ocaml has widely acknowledged benefits for engineers, mostly derived from a sophistocated type system, compile-time checking which eliminates several classes of common programming errors, and good native performance. While all of this is wonderful, there are well-written C and C++ libraries that Ocaml users will want to take advantage of as part of their arsenal (such as SSL and gdbm), as well as their own mature C and C++ code. SWIG allows this code to be used in a natural, type-safe way with Ocaml, by providing the necessary, but repetetive glue code which creates and uses Ocaml values to communicate with C and C++ code. In addition, SWIG also produces the needed Ocaml source that binds variants, functions, classes, etc.

16.1 Preliminaries

SWIG 1.3 works with Ocaml 3.04 and above. Given the choice, you should use the latest stable release. The SWIG Ocaml module has been tested on Linux (m68k,MIPS,Intel) and Cygwin on Windows. The best way to determine whether your system will work is to compile the examples and test-suite which come with SWIG. You can do this by running make check from the SWIG root directory after installing SWIG. The Ocaml module has been tested using the system's dynamic linking (the usual -lxxx against libxxx.so, but not using the explicit dynamic linking provided by the Dl package http://www.ocaml-programming.de/packages/documentation/dl/ , although I suspect that it will work without a problem. If anyone would like to evaluate this support, I will share the results here.

16.1.1 Running SWIG

The basics of getting a SWIG Ocaml module up and running can be seen from one of SWIG's example Makefiles, but is also described here. To build an Ocaml module, run SWIG using the -ocaml option.
%swig -ocaml example.i

This will produce 3 files. The file example_wrap.c contains all of the C code needed to build an Ocaml module. To build the module, you will compile the file example_wrap.c with ocamlc or ocamlopt to create the needed .o file. You will need to compile the resulting .ml and .mli files as well, and do the final link with -custom (not needed for native link).

16.1.2 Getting the right header files

You may need the libswigocaml.h file that comes with the distribution to be included. It provides several useful functions that almost all programs that use SWIG will need. It is located in $(prefix)/include/libswigocaml.h where $(prefix) is usually /usr/local, but could be /usr. This is set at configure time. The functions most frequently used in here are isnull and nullptr for creating and checking null pointers.

16.1.3 Compiling the code

Use ocamlc or ocamlopt to compile your SWIG interface like:

% ocamlc -c -ccopt "-I/usr/include/foo -I/usr/local/include" example_wrap.c
% ocamlc -c example.mli
% ocamlc -c example.ml

ocamlc is aware of .c files and knows how to handle them. Unfortunately, it does not know about .cxx, .cc, or .cpp files, so when SWIG is invoked in C++ mode, you must:

% cp example_wrap.cxx example_wrap.cxx.c
% ocamlc -c ... -ccopt -xc++ example_wrap.cxx.c
% ...

16.1.4 The camlp4 module

The camlp4 module (swig.ml -> swig.cmo) contains a simple rewriter which makes C++ code blend more seamlessly with objective caml code. It's use is optional. The source file is included in the Lib/ocaml directory of the SWIG source distribution.

The basic principle of the module is to recognize certain non-caml expressions and convert them for use with C++ code as interfaced by SWIG. The camlp4 module is written to work with generated SWIG interfaces, and probably isn't great to use with anything else.

Here are the main rewriting rules:

InputRewritten to
f'( ... ) as in
atoi'("0") or
_exit'(0)
f(C_list [ ... ]) as in
atoi (C_list [ C_string "0" ]) or
_exit (C_list [ C_int 0 ])
object -> "method" ( ... )(invoke object) "method" (C_list [ ... ])
object 'binop argument as in
a '+= b
(invoke object) "+=" argument as in
(invoke a) "+=" b
Note that because camlp4 always recognizes << and >>, they are replaced by lsl and lsr in operator names.
'unop object as in
'! a
(invoke a) "!" C_void
Smart pointer access like this object '-> "method" ( args ) (invoke (invoke object "->" C_void))
Invoke syntax object . '( ... ) (invoke object) "()" (C_list [ ... ])
Array syntax object '[ 10 ] (invoke object) "[]" (C_int 10)

16.1.5 Current thoughts on best practice for Ocaml

Because the VC compiler (cl) needs link options specified after all compiler options, and ocamlc doesn't really understand that, I think that this is the best way to link ocaml code with C++ code. I formulated this method to make it easy for co-workers who rely on MSDev to create GUIs, etc.. to live in harmony with the ocaml parts of the application.

Let's say you have ocaml sources foo.ml and bar.ml and interface frob.i;

swig -ocaml -c++ frob.i
ocamlc -custom -c frob.mli
ocamlc -custom -c frob.ml
cp frob_wrap.cxx frob_wrap.c
ocamlc -custom -c -I$(FROBLIB)/include frob_wrap.c
ocamlc -custom -c foo.ml
ocamlc -custom -c bar.ml
ocamlc -pack -o foobar.cmo foo.cmo bar.cmo frob.cmo
ocamlc -custom -output-obj -o foobar.obj foobar.cmo

At this point, foobar.obj can be included in your MSVC project and linked against other code. This is how you link it:

link /OUT:big_program.exe \
    other1.obj other2.obj foobar.obj frob_wrap.obj \
    $(OCAMLLIB)/ocamlrun.lib $(FROBLIB)/lib/frob.lib

16.1.6 Using your module

You can test-drive your module by building a toplevel ocaml interpreter. Consult the ocaml manual for details.

When linking any ocaml bytecode with your module, use the -custom option to build your functions into the primitive list. This option is not needed when you build native code.

16.1.7 Compilation problems and compiling with C++

As mentioned above, .cxx files need special handling to be compiled with ocamlc. Other than that, C code that uses class as a non-keyword, and C code that is too liberal with pointer types may not compile under the C++ compiler. Most code meant to be compiled as C++ will not have problems.

16.2 The low-level Ocaml/C interface

In order to provide access to overloaded functions, and provide sensible outputs from them, all C entites are represented as members of the c_obj type:

In the code as seen by the typemap writer, there is a value, swig_result, that always contains the current return data. It is a list, and must be appended with the caml_list_append function, or with functions and macros provided by objective caml.

type c_obj =
    C_void
  | C_bool of bool
  | C_char of char
  | C_uchar of char
  | C_short of int
  | C_ushort of int
  | C_int of int
  | C_uint of int32
  | C_int32 of int32
  | C_int64 of int64
  | C_float of float
  | C_double of float
  | C_ptr of int64 * int64
  | C_array of c_obj array
  | C_list of c_obj list
  | C_obj of (string -> c_obj -> c_obj)
  | C_string of string
  | C_enum of c_enum_t
A few functions exist which generate and return these:
Because of this style, a typemap can return any kind of value it wants from a function.  This enables out typemaps and inout typemaps to work well.  The one thing to remember about outputting values is that you must append them to the return list with swig_result = caml_list_append(swig_result,v).

 This function will return a new list that has your element appended. Upon return to caml space, the fnhelper function beautifies the result. A list containing a single item degrades to only that item (i.e. [ C_int 3 ] -> C_int 3), and a list containing more than one item is wrapped in C_list (i.e. [ C_char 'a' ; C_char 'b' -> C_list [ C_char 'a' ; C_char b ]).  This is in order to make return values easier to handle when functions have only one return value, such as constructors, and operators.  In addition, string, pointer, and object values are interchangable with respect to caml_ptr_val, so you can allocate memory as caml strings and still use the resulting pointers for C purposes, even using them to construct simple objects on. Note, though, that foreign C++ code does not respect the garbage collector, although the SWIG interface does.

The wild card type that you can use in lots of different ways is C_obj. It allows you to wrap any type of thing you like as an object using the same mechanism that the ocaml module does.  When evaluated in caml_ptr_val, the returned value is the result of a call to the object's "&" operator, taken as a pointer.

You should only construct values using objective caml, or using the functions caml_val_* functions provided as static functions to a SWIG ocaml module, as well as the caml_list_* functions. These functions provide everything a typemap needs to produce values. In addition, value items pass through directly, but you must make your own type signature for a function that uses value in this way.

16.2.1 The generated module

The SWIG %module directive specifies the name of the Ocaml module to be generated. If you specified `%module example', then your Ocaml code will be accessible in the module Example. The module name is always capitalized as is the ocaml convention. Note that you must not use any Ocaml keyword to name your module. Remember that the keywords are not the same as the C++ ones.

You can introduce extra code into the output wherever you like with SWIG. These are the places you can introduce code:
"header"This code is inserted near the beginning of the C wrapper file, before any function definitions.
"wrapper"This code is inserted in the function definition section.
"runtime"This code is inserted near the end of the C wrapper file.
"mli"This code is inserted into the caml interface file. Special signatures should be inserted here.
"ml"This code is inserted in the caml code defining the interface to your C code. Special caml code, as well as any initialization which should run when the module is loaded may be inserted here.

16.2.2 Enums

SWIG will wrap enumerations as polymorphic variants in the output Ocaml code, as above in C_enum.  In order to support all C++-style uses of enums, the function int_to_enum and enum_to_int are provided for ocaml code to produce and consume these values as integers.  Other than that, correct uses of enums will not have a problem.  Since enum labels may overlap between enums, the enum_to_int and int_to_enum functions take an enum type label as an argument. Example:

%module enum_test
%{
enum c_enum_type { a = 1, b, c = 4, d = 8 };
%}
enum c_enum_type { a = 1, b, c = 4, d = 8 };

The output mli contains:

type c_enum_type = [
  `unknown
| `c_enum_type
]
type c_enum_tag = [
  `int of int
| `a
| `b
| `c
| `d
]
val int_to_enum c_enum_type -> int -> c_obj
val enum_to_int c_enum_type -> c_obj -> c_obj
So it's possible to do this:
bash-2.05a$ ocamlmktop -custom enum_test_wrap.o enum_test.cmo -o enum_test_top
bash-2.05a$ ./enum_test_top 
        Objective Caml version 3.04

# open Enum_test ;;
# let x = C_enum `a ;;
val x : Enum_test.c_obj = C_enum `a
# enum_to_int `c_enum_type x ;;
- : Enum_test.c_obj = C_int 1
# int_to_enum `c_enum_type 4 ;;
- : Enum_test.c_obj = C_enum `c

16.2.3 C++ Classes

C++ classes, along with structs and unions are represented by C_obj (string -> c_obj -> c_obj) wrapped closures.  These objects contain a method list, and a type, which allow them to be used like C++ objects. When passed into typemaps that use pointers, they degrade to pointers through their "&" method.  Every method an object has is represented as a string in the object's method table, and each method table exists in memory only once.  In addition to any other operators an object might have, certain builtin ones are provided by SWIG: (all of these take no arguments (C_void))
"~"Delete this object
"&"Return an ordinary C_ptr value representing this object's address
":methods"Returns a list of strings containing the names of the methods this object contains
":classof"Returns the name of the class this object belongs to.
":parents"Returns a list of all direct parent classes which have been wrapped by SWIG.
"::[parent-class]"Returns a view of the object as the indicated parent class. This is mainly used internally by the SWIG module, but may be useful to client programs.
"[member-variable]"Each member variable is wrapped as a method with an optional parameter. Called with one argument, the member variable is set to the value of the argument. With zero arguments, the value is returned.
Note that this string belongs to the wrapper object, and not the underlying pointer, so using create_[x]_from_ptr alters the returned value for the same object.

16.2.3.1 C++ Class Example

Here's a simple example using Trolltech's Qt Library:
qt.i
%module qt
%{
#include <qapplication.h>
#include <qpushbutton.h>
%}
class QApplication {
public:
        QApplication( int argc, char **argv );
        void setMainWidget( QWidget *widget );
        void exec();
};

class QPushButton {
public:
        QPushButton( char *str, QWidget *w );
        void resize( int x, int y );
        void show();
};

16.2.3.2 Compiling the example

bash-2.05a$ QTPATH=/your/qt/path
bash-2.05a$ swig -ocaml -c++ -I$QTPATH/include  qt.i
bash-2.05a$ mv qt_wrap.cxx qt_wrap.c
bash-2.05a$ ocamlc -c -ccopt -xc++ -ccopt -g -g -ccopt -I$QTPATH/include qt_wrap.c 
bash-2.05a$ ocamlc -c qt.mli
bash-2.05a$ ocamlc -c qt.ml
bash-2.05a$ ocamlmktop -custom qt_wrap.o qt.cmo -o qt_top -cclib -L$QTPATH/lib -cclib -lqt

16.2.3.3 Sample Session

bash-2.05a$ ./qt_top 
        Objective Caml version 3.06

# open Qt ;;
# let a = new_QApplication (C_list [ C_int 0; C_int 0 ]) ;;
val a : Qt.c_obj = C_obj 
# let hello = new_QPushButton (C_list [ C_string "hi" ; C_int 0 ]) ;;
val hello : Qt.c_obj = C_obj 
# (invoke hello) "resize" (C_list [ C_int 100 ; C_int 30 ]) ;;
- : Qt.c_obj = C_void
# (invoke hello) "show" C_void ;;
- : Qt.c_obj = C_void
# (invoke a) "exec" C_void ;;

Or with the camlp4 module:

bash-2.05a$ ./qt_top 
        Objective Caml version 3.06

# #load "camlp4o.cma" ;;
        Camlp4 Parsing version 3.06

# #load "./swig.cmo" ;;
# open Qt ;;
# let a = new_QApplication '(0,0) ;;
val a : Qt.c_obj = C_obj 
# let hello = new_QPushButton '("hi",0) ;;
val hello : Qt.c_obj = C_obj 
# hello -> "resize" (100,30) ;;
- : Qt.c_obj = C_void
# hello -> "show" () ;;
- : Qt.c_obj = C_void
# a -> "exec" () ;;

In either case, assuming you have a working installation of QT, you will see a window containing the string "hi" in a button.

16.2.4 Exceptions

Catching exceptions is now supported using SWIG's %exception feature. A simple but not too useful example is provided by the throw_exception testcase in Examples/test-suite. You can provide your own exceptions, too.