%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).
% 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
% ...
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:
Input | Rewritten 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) |
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
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.
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.
A few functions exist which generate and return these: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
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.
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. |
%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:
So it's possible to do this: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
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
"~" | 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. |
%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(); }; |
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
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.