%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 example %{ #include "example.h" %} %include stl.i namespace std { %template(StringVector) std::vector |
This example is in Examples/ocaml/stl |
Since there's a makefile in that directory, the example is easy to build.
Here's a sample transcript of an interactive session using a string vector after making a toplevel like this:
ocamlmktop -custom -cclib -g -ccopt -g -g \ example_wrap.o example.cmo -o example_top -cclib -lstdc++
bash-2.05a$ ./example_top Objective Caml version 3.06 # open Example ;; # let x = new_StringVector C_void ;; val x : Example.c_obj = C_obj# (invoke x) ":methods" C_void ;; - : Example.c_obj = C_list [C_string "nop"; C_string "size"; C_string "empty"; C_string "clear"; C_string "push_back"; C_string "[]"; C_string "="; C_string "set"; C_string "~"; C_string "&"; C_string ":parents"; C_string ":classof"; C_string ":methods"] # (invoke x) "push_back" (C_string "foo") ;; - : Example.c_obj = C_void # (invoke x) "push_back" (C_string "bar") ;; - : Example.c_obj = C_void # (invoke x) "push_back" (C_string "baz") ;; - : Example.c_obj = C_void # (invoke x) "[]" (C_int 1) ;; - : Example.c_obj = C_string "bar" # (invoke x) "set" (C_list [ C_int 1 ; C_string "spam" ]) ;; - : Example.c_obj = C_void # (invoke x) "[]" (C_int 1) ;; - : Example.c_obj = C_string "spam" #
%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.
You can turn on director classes by using an optional module argument like this:
%module(directors="1") ... // Turn on the director class for a specific class like this: %feature("director") class foo { ... };
Because the Ocaml language module treats C++ method calls as calls to a certain function, all you need to do is to define the function that will handle the method calls in terms of the public methods of the object, and any other relevant information. The function new_derived_object uses a stub class to call your methods in place of the ones provided by the underlying implemenation. The object you receive is the underlying object, so you are free to call any methods you want from within your derived method. Note that calls to the underlying object do not invoke Ocaml code. You need to handle that yourself.
new_derived_object receives your function, the function that creates the underlying object, and any constructor arguments, and provides an object that you can use in any usual way. When C++ code calls one of the object's methods, the object invokes the Ocaml function as if it had been invoked from Ocaml, allowing any method definitions to override the C++ ones.
In this example, I'll examine the objective caml code involved in providing an overloaded class. This example is contained in Examples/ocaml/shapes.
open Example ... let triangle_class pts ob meth args = match meth with "cover" -> (match args with C_list [ x_arg ; y_arg ] -> C_bool (point_in_triangle pts (get_float x_arg) (get_float y_arg)) | _ -> raise (Failure "cover needs two double arguments.")) | _ -> (invoke ob) meth args ;; let triangle = new_derived_object new_shape (triangle_class ((0.0,0.0),(0.5,1.0),(1.0,0.0))) C_void ;; let _ = _draw_shape_coverage (C_list [ triangle ; C_int 60 ; C_int 20 ]) ;; |
This is the meat of what you need to do. The actual "class" definition containing the overloaded method is defined in the function triangle_class. This is a lot like the class definitions emitted by SWIG, if you look at example.ml, which is generated when SWIG consumes example.i. Basically, you are given the arguments as a c_obj and the method name as a string, and you must intercept the method you are interested in and provide whatever return value you need. Bear in mind that the underlying C++ code needs the right return type, or an exception will be thrown. This exception will generally be Failure, or NotObject. That having been said, your method will be called whenever the underlying method that it depends on will be called, but only when the __up pointer is not set. Therefore, if you need to rely on other methods from your own object, that you also have overrides for, you'll need to call them yourself, rather than relying on the normal method interface, because the underlying code won't call you back a second time. This mechanism prevents infinite recursion on method calls, and allows the cathall case at the end of the example code to succeed in calling a method of the underlying object.
In the example, the draw_shape_coverage function plots the indicated number of points as either covered (x) or uncovered ( ) between 0 and 1 on the X and Y axes. Your shape implementation can provide any coverage map it likes, as long as it responds to the "cover" method call with a boolean return (the underlying method returns bool). This might allow a tricky shape implementation, such as a boolean combination, to be expressed in a more effortless style in ocaml, while leaving the "engine" part of the program in C++.
The definition of the actual object triangle can be described this way:
let triangle = new_derived_object new_shape (triangle_class ((0.0,0.0),(0.5,1.0),(1.0,0.0))) C_void
The first argument to new_derived_object, new_shape is the method which returns a shape instance. This function will be invoked with the third argument will be appended to the argument list [ C_void ]. In the example, the actual argument list is sent as (C_list [ C_void ; C_void ]). The augmented constructor for a director class needs the first argument to determine whether it is being constructed as a derived object, or as an object of the indicated type only (in this case shape). The Second argument is a closure that will be added to the final C_obj.
The actual object passed to the self parameter of the director object will be a C_director_core, containing a c_obj option ref and a c_obj. The c_obj provided is the same object that will be returned from new_derived object, that is, the object exposing the overridden methods. The other part is an option ref that will have its value extracted before becoming the ob parameter of your class closure. This ref will contain None if the C++ object underlying is ever destroyed, and will consequently trigger an exception when any method is called on the object after that point (the actual raise is from an inner function used by new_derived_object, and throws NotObject).