[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [MirageOS-devel] ctypes and c++
On 10/05/2014, Richard Mortier <Richard.Mortier@xxxxxxxxxxxxxxxx> wrote: > On 9 May 2014, at 17:42, Jeremy Yallop <jeremy.yallop@xxxxxxxxxxxx> wrote: >> On 9 May 2014 17:05, Steven Luland <psxsl6@xxxxxxxxxxxxxxxx> wrote: >>> Iâm looking to use ctypes to make the openzwave library compatible with >>> OCaml. Would ctypes work on a c++ library such as this? >> >> For the moment, ctypes needs a C-compatible interface. It's possible >> to bind C++ libraries that expose 'extern "C"' declarations, but not >> possible to bind arbitrary C++ code. > > apologies, it's been a while since i did any C++ (thankfully)-- does this > need more support than just name mangling? (which could be done manually in > a pinch couldn't it)? Indeed, if you're willing to do name mangling manually it should be possible to get something working. Here's a simple example: a C++ library with a single trivial class whose instances can be printed out. To make it a tiny bit more realistic I've made the output stream an argument of the print function, so we need to look up symbols in the standard library as well as the library we're looking to bind. $ cat point.cc #include <ostream> class Point { int x, y; public: Point(int, int); ~Point(); void print(std::ostream&); }; Point::Point(int x_, int y_) : x(x_), y(y_) { } Point::~Point() { } void Point::print(std::ostream& os) { os << "<" << x << "," << y << ">\n"; } $ g++ -shared -ansi -pedantic -W -Wall -fPIC point.cc -o libpoint.so Before we get started, let's look up the symbols we need. We'll want cout from the standard library: $ objdump -T /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep cout 0000000000301280 g DO .bss 0000000000000110 GLIBCXX_3.4 _ZSt5wcout 0000000000301700 g DO .bss 0000000000000110 GLIBCXX_3.4 _ZSt4cout The second symbol is the one we want; the first is for wide character output. We'll also need the constructor and destructor of Point, and the print member function: $ objdump -T libpoint.so | grep Point 0000000000000986 g DF .text 0000000000000023 Base _ZN5PointC2Eii 00000000000009aa g DF .text 0000000000000024 Base _ZN5PointD2Ev 00000000000009aa g DF .text 0000000000000024 Base _ZN5PointD1Ev 0000000000000986 g DF .text 0000000000000023 Base _ZN5PointC1Eii 00000000000009ce g DF .text 0000000000000070 Base _ZN5Point5printERSo I think the C1 and D1 constructor and destructor are the ones we want. The C2 and D2 versions are used for object creation and deletion from subclasses. We're ready to bind libpoint from ctypes. Let's start by opening the Ctypes module and loading the shared objects for libpoint and the standard library: open Ctypes let libpoint = Dl.(dlopen ~filename:"./libpoint.so" ~flags:[RTLD_NOW]) let libstdcpp = Dl.(dlopen ~filename:"libstdc++.so.6" ~flags:[RTLD_NOW]) We'll define an opaque type to represent std::ostream, then bind std::cout using the symbol we looked up earlier: let ostream = structure "ostream" let cout = Foreign.foreign_value ~from:libstdcpp "_ZSt4cout" ostream Let's describe the layout of the Point class. (This isn't strictly necessary, since we're not going to access the fields directly. We could make do with just knowing the size.) type point let point = structure "point" let x = field point "x" int let y = field point "y" int let () = seal point Time to bind the constructor, destructor, and print function. They all accept the 'this' pointer as an additional first argument. let construct_Point = Foreign.foreign ~from:libpoint "_ZN5PointC1Eii" (ptr point @-> int @-> int @-> returning void) let destroy_Point = Foreign.foreign ~from:libpoint "_ZN5PointD1Ev" (ptr point @-> returning point) let print_Point = Foreign.foreign ~from:libpoint "_ZN5Point5printERSo" (ptr point @-> ptr ostream @-> returning void) Before we can call the constructor we need to allocate memory for the object. The new_Point function, defined next, allocates memory for a Point, attaches a finaliser which calls the destructor when the memory is garbage collected, and calls the constructor to initialize the object: let new_Point ~x ~y = let finalise p = ignore (destroy_Point (addr p)) in let p = make ~finalise point in begin construct_Point (addr p) x y; p end Finally, we can use our new bindings. Let's allocate a Point and call its print() method with std::cout: let pt = new_Point ~x:10 ~y:20 let () = print_Point (addr pt) cout It works on my machine, at least! Here's the output: <10,20> Of course, this is a fairly trivial example. Things become more interesting (or in some cases impossible with current ctypes) if the C++ library you want to bind uses the many exciting features of C++: various forms of inheritance, templates, and so on. >> Recent developments (namely >> support for generating stubs) make binding to C++ feasible, so it's >> possible that there'll be support for C++ at some point. (What I mean by this is that with stub generation the C++ compiler will take care of name mangling, since ctypes is generating source rather than resolving symbols in binary objects. There are various missing pieces making that route unviable for the moment, though.) > cool-- i guess this isn't terribly high priority though? or is it something > that's actually planned? For now it's not so much planned as noted as a nice-to-have (https://github.com/ocamllabs/ocaml-ctypes/issues/32). Jeremy. _______________________________________________ MirageOS-devel mailing list MirageOS-devel@xxxxxxxxxxxxxxxxxxxx http://lists.xenproject.org/cgi-bin/mailman/listinfo/mirageos-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |