[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [MirageOS-devel] C++ library to OCaml: OCaml objects



Hi Steven,

On 20 June 2014 16:10, Steven Luland <psxsl6@xxxxxxxxxxxxxxxx> wrote:
> I wanted to set the type to work with the # notation that OCaml uses

I find it easier to think about this kind of thing if I separate the
code that binds the C++ from the code that gives it a more idiomatic
OCaml interface.  You then end up with four "layers" with
clearly-separated responsibilities:

  (1) the C++ library itself (object-oriented C++)
  (2) the 'extern "C"' interface (function-oriented C++)
  (3) the ctypes bindings (function-oriented OCaml)
  (4) the OCaml interface (object-oriented OCaml)

Here's an example to show what I mean.  First, a simple C++ library
with a thin 'extern "C"' interface that takes "this" arguments and
forwards calls to member functions:

   $ cat shapes.cc
   #include <cmath>

   struct Shape {
     virtual double area() = 0;
     virtual ~Shape() = 0;
   };

   Shape::~Shape() { }

   struct Square : public Shape {
     Square(double s) : side(s) { }
     double area() { return side * side; }
     ~Square() { }

   private:
     double side;
   };

   struct Circle : public Shape {
     Circle(double r) : radius(r) { }
     double area() { return M_PI * radius * radius; }
     ~Circle() { }

   private:
    double radius;
   };

   extern "C" {
     Square *create_Square(double side) { return new Square(side); }
     void destroy_Square(Square *s) { delete s; }
     double Square_area(Square* s) { return s->area(); }

     Circle *create_Circle(double radius) { return new Circle(radius); }
     void destroy_Circle(Circle *c) { delete c; }
     double Circle_area(Circle* c) { return c->area(); }
   }
   $ g++ -shared -fPIC -ansi -pedantic -W -Wall shapes.cc -o libshapes.so

You could also have a header file declaring the extern functions, but
let's leave that out for the sake of simplicity, since ctypes.foreign
doesn't use it.

The next layer uses ctypes to bind the extern "C" functions.  This is
a fairly straightforward matter of translating the C declaration
syntax into the corresponding calls to functions in the ctypes
interface.

   $ cat shape_bindings.ml
   open Ctypes
   open Foreign

   type square
   type circle
   let square : square structure typ = structure "Square"
   let circle : circle structure typ = structure "Circle"

   let create_Square =
     foreign "create_Square" (double @-> returning (ptr square))
   let destroy_Square =
     foreign "destroy_Square" (ptr square @-> returning void)
   let square_area =
     foreign "Square_area" (ptr square @-> returning double)

   let create_Circle =
     foreign "create_Circle" (double @-> returning (ptr circle))
   let destroy_Circle =
     foreign "destroy_Circle" (ptr circle @-> returning void)
   let circle_area =
     foreign "Circle_area" (ptr circle @-> returning double);

Finally, we can define OCaml classes that forward method calls to the
various bound functions.  There are various choices, such as how do
deal with destructors, that probably need to be made on a per-binding
basis.  For this example, I've defined an initializer
(http://caml.inria.fr/pub/docs/manual-ocaml-400/manual005.html#toc21)
in each class that registers the destructor of each object with the
garbage collector so that the C++ object is destroyed when the
corresponding OCaml object becomes unreachable.  I've also made the
'this' member, which is implicit in C++, into an explicit instance
variable in the OCaml classes.

   $ cat shapes.ml
   class virtual shape =
   object
     method virtual area : float
   end

   class circle ~radius =
   object
     inherit shape
     val this = Shape_bindings.create_Circle radius
     method area = Shape_bindings.circle_area this
     initializer Gc.finalise Shape_bindings.destroy_Circle this
   end

   class square ~side =
   object
     inherit shape
     val this = Shape_bindings.create_Square side
     method area = Shape_bindings.square_area this
     initializer Gc.finalise Shape_bindings.destroy_Square this
   end
   $ ocamlfind ocamlc -linkpkg -custom -package ctypes.foreign \
        shape_bindings.ml shapes.ml -cclib -L. -cclib -lshapes

To see it all working we can load the library, instantiate the
objects, and call methods:

   $ ocamlfind ocamlmktop -package ctypes.foreign shapes.cma -o shapes.top
   $ ./shapes.top  -short-paths
           OCaml version 4.01.0

   # open Shapes;;
   # let c = new circle ~radius:5.0;;
   val c : circle = <obj>
   # let s = new square ~side:10.0;;
   val s : square = <obj>
   # c#area;;
   - : float = 78.5398163397448315
   # s#area;;
   - : float = 100.

Hope that helps a bit!

Jeremy

_______________________________________________
MirageOS-devel mailing list
MirageOS-devel@xxxxxxxxxxxxxxxxxxxx
http://lists.xenproject.org/cgi-bin/mailman/listinfo/mirageos-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.