From: Guillaume Yziquel <guillaume.yziquel@citycable.ch>
To: Luca de Alfaro <luca@dealfaro.org>
Cc: Inria Ocaml Mailing List <caml-list@inria.fr>
Subject: Re: [Caml-list] How to wrap around C++?
Date: Mon, 08 Feb 2010 13:38:30 +0100 [thread overview]
Message-ID: <4B7005C6.6030201@citycable.ch> (raw)
In-Reply-To: <28fa90931002071813k7330ad34s7a2ec8b4cf1c3d11@mail.gmail.com>
Luca de Alfaro a écrit :
> I need to be able to call some C++ functions from my Ocaml code.
> Can someone point me to some examples on how this is done? Is it an issue
> if what I need to wrap is C++, rather than the more standard C?
>
> I also would like to know if I can do something more complex... namely, I
> would like to create a C++ object, and return it to Ocaml.
> Ocaml then needs to be able to call methods of that object. The object does
> not need to be directly accessible from Ocaml, but it needs to be
> persistent, so that all Ocaml calls refer to the same object.
>
> Essentially, the C++ object implements access to a file via some
> compression, etc, mechanism. In C++, one creates the object, calls write
> and read methods, calls the method for closing the file, and deletes the
> object.
>
> How can I wrap around such an object in Ocaml? Is it possible? Any
> advice?
>
> Many thanks,
>
> Luca
As mentioned earlier there are Swig bindings from Art Yerkes that do
work with OCaml. Unfortunately the documentation is rather terse, and
you have code that believes somehow that OCaml is dynamically typed: You
pass list as arguments, there is some runtime type checking done at the
interface, and you may have to use Obj.magic on OCaml side.
I have started rewritting Swig bindings to make them static and
hopefully more efficient. I nevertheless stopped (and I hope to come
back to it) for two reasons: lack of time, and a major shortcoming on
Swig side: typemaps do not allow to construct OCaml polymorphic type.
This means that you have to declare an intlist type instead of an int
list type. To amend that, you have to dig into Swig's internals. While
possible, it is rather painful to do. And you really feel that Swig is
more aimed at dynamically typed languages wishing to interface with C++
than at statically typed languages.
You can find my bitroting Swig module here:
http://swig.svn.sourceforge.net/viewvc/swig/branches/yziquel-ocaml/
(If someone feels like pursuing this Swig endeavour, I'll gladly help
out...)
Here's an example of what you can obtain with Swig (but if I were you,
I'd definitely do the bindings manually). You can go directly at the
bottom of the email for the stub code per se. As you can see, you have
to use extern "C" to be able to interface C++ with the C interface of
OCaml. Concerning C++ objects, things depends. For instance, if the
object is allocated on the stack, there's no way you can bring it back
to OCaml. If it's allocated with new, you can indeed bring it back to
OCaml. You simply have to register finalisers that trigger the
destructor of the object once the OCaml GC reclaims it. (Assuming that
no other OCaml values holds the object, that is...)
For the sake of exhaustivity, my Makefile was
> all: freeling.cma
>
> freeling.cma: dllfreeling_stubs.so freeling.cmo freeling.cmo
> ocamlc -a -dllib dllfreeling_stubs.so -dllib libmorfo.so -dllib libfries.so -dllib libpcre.so -dllib libomlet.so -dllib libdb_cxx.so -o freeling.cma freeling.cmo freeling.cmo
>
> freeling.cmo: freeling.ml
> ocamlc -c freeling.ml
>
> freeling.cmo: freeling_wrap.cxx freeling.mli freeling.ml
> ocamlc -c freeling.mli
> ocamlc -c freeling.ml
>
> dllfreeling_stubs.so: libfreeling_stubs.a freeling_wrap.cxx.o
> ocamlmklib -o freeling_stubs freeling_wrap.cxx.o
>
> libfreeling_stubs.a: freeling_wrap.cxx.o
> ar rcs libfreeling_stubs.a freeling_wrap.cxx.o
>
> freeling_wrap.cxx.o: freeling_wrap.cxx.c
> gcc -fPIC -O2 -c -xc++ freeling_wrap.cxx.c
>
> freeling_wrap.cxx.c: freeling_wrap.cxx
> cp freeling_wrap.cxx freeling_wrap.cxx.c
>
> freeling_wrap.cxx:
> swig -ocaml -c++ freeling.i
>
> clean:
> rm -f freeling.ml
> rm -f freeling.mli
> rm -f freeling_wrap.cxx
> rm -f freeling_wrap.cxx.c
> rm -f freeling.cmi
> rm -f freeling.cmo
> rm -f freeling_wrap.cxx.o
The Swig interface file, freeling.i:
> %module freeling
> %{
> #include "freeling.h"
> %}
>
> %include <std_string.i>
> %include <std_list.i>
>
> %nodefaultdtor;
>
> %template() std::list<word>;
>
> class tokenizer {
> public:
> /// Constructor
> tokenizer(const std::string &);
>
> /// tokenize string with default options
> std::list<word> tokenize(const std::string &);
> /// tokenize string with default options, tracking offset in given int param.
> //std::list<word> tokenize(const std::string &, unsigned long &);
> };
Was generating the following on the OCaml side:
> module Swig = struct
>
> (* The Swig module contains raw accessors to C functions,
> as well as type declarations enforcing sound typing. *)
>
> type tokenizer
> external _wrap_new_tokenizer : string -> tokenizer = "_wrap_new_tokenizer"
> external _wrap_tokenizer_tokenize : tokenizer -> string -> ocaml_typeparm[word] list = "_wrap_tokenizer_tokenize"
>
> end;;
>
> class virtual _opaque_tokenizer = object(self)
> val virtual underlying_cpp_object : Swig.tokenizer
> method tokenize = Swig._wrap_tokenizer_tokenize underlying_cpp_object
> end;;
>
> class type tokenizer = object
> method tokenize : string -> ocaml_typeparm[word] list
> end;;
>
> class tokenizer_0 arg0 = object(self)
> inherit _opaque_tokenizer
> val underlying_cpp_object = Swig._wrap_new_tokenizer arg0
> end;;
with the following .mli file:
> class type tokenizer = object
> method tokenize : string -> ocaml_typeparm[word] list
> end
>
> class tokenizer_0 : string -> tokenizer
and the following C stub code:
> #ifdef __cplusplus
> /* SwigValueWrapper is described in swig.swg */
> template<typename T> class SwigValueWrapper {
> struct SwigMovePointer {
> T *ptr;
> SwigMovePointer(T *p) : ptr(p) { }
> ~SwigMovePointer() { delete ptr; }
> SwigMovePointer& operator=(SwigMovePointer& rhs) { T* oldptr = ptr; ptr = 0; delete oldptr; ptr = rhs.ptr; rhs.ptr = 0; return *this; }
> } pointer;
> SwigValueWrapper& operator=(const SwigValueWrapper<T>& rhs);
> SwigValueWrapper(const SwigValueWrapper<T>& rhs);
> public:
> SwigValueWrapper() : pointer(0) { }
> SwigValueWrapper& operator=(const T& t) { SwigMovePointer tmp(new T(t)); pointer = tmp; return *this; }
> operator T&() const { return *pointer.ptr; }
> T *operator&() { return pointer.ptr; }
> };
>
> template <typename T> T SwigValueInit() {
> return T();
> }
> #endif
>
> /* -----------------------------------------------------------------------------
> * This section contains generic SWIG labels for method/variable
> * declarations/attributes, and other compiler dependent labels.
> * ----------------------------------------------------------------------------- */
>
> /* template workaround for compilers that cannot correctly implement the C++ standard */
> #ifndef SWIGTEMPLATEDISAMBIGUATOR
> # if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560)
> # define SWIGTEMPLATEDISAMBIGUATOR template
> # elif defined(__HP_aCC)
> /* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */
> /* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */
> # define SWIGTEMPLATEDISAMBIGUATOR template
> # else
> # define SWIGTEMPLATEDISAMBIGUATOR
> # endif
> #endif
>
> /* inline attribute */
> #ifndef SWIGINLINE
> # if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__))
> # define SWIGINLINE inline
> # else
> # define SWIGINLINE
> # endif
> #endif
>
> /* attribute recognised by some compilers to avoid 'unused' warnings */
> #ifndef SWIGUNUSED
> # if defined(__GNUC__)
> # if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
> # define SWIGUNUSED __attribute__ ((__unused__))
> # else
> # define SWIGUNUSED
> # endif
> # elif defined(__ICC)
> # define SWIGUNUSED __attribute__ ((__unused__))
> # else
> # define SWIGUNUSED
> # endif
> #endif
>
> #ifndef SWIG_MSC_UNSUPPRESS_4505
> # if defined(_MSC_VER)
> # pragma warning(disable : 4505) /* unreferenced local function has been removed */
> # endif
> #endif
>
> #ifndef SWIGUNUSEDPARM
> # ifdef __cplusplus
> # define SWIGUNUSEDPARM(p)
> # else
> # define SWIGUNUSEDPARM(p) p SWIGUNUSED
> # endif
> #endif
>
> /* internal SWIG method */
> #ifndef SWIGINTERN
> # define SWIGINTERN static SWIGUNUSED
> #endif
>
> /* internal inline SWIG method */
> #ifndef SWIGINTERNINLINE
> # define SWIGINTERNINLINE SWIGINTERN SWIGINLINE
> #endif
>
> /* exporting methods */
> #if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
> # ifndef GCC_HASCLASSVISIBILITY
> # define GCC_HASCLASSVISIBILITY
> # endif
> #endif
>
> #ifndef SWIGEXPORT
> # if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
> # if defined(STATIC_LINKED)
> # define SWIGEXPORT
> # else
> # define SWIGEXPORT __declspec(dllexport)
> # endif
> # else
> # if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)
> # define SWIGEXPORT __attribute__ ((visibility("default")))
> # else
> # define SWIGEXPORT
> # endif
> # endif
> #endif
>
> /* calling conventions for Windows */
> #ifndef SWIGSTDCALL
> # if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
> # define SWIGSTDCALL __stdcall
> # else
> # define SWIGSTDCALL
> # endif
> #endif
>
> /* Deal with Microsoft's attempt at deprecating C standard runtime functions */
> #if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
> # define _CRT_SECURE_NO_DEPRECATE
> #endif
>
> /* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */
> #if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE)
> # define _SCL_SECURE_NO_DEPRECATE
> #endif
>
>
>
> #include <stdlib.h>
> #include <string.h>
> #include <stdio.h>
>
> /* Including OCaml system. */
> #define CAML_VALUE value
> extern "C" {
> #include <caml/alloc.h>
> #include <caml/custom.h>
> #include <caml/mlvalues.h>
> #include <caml/memory.h>
> #include <caml/callback.h>
> #include <caml/fail.h>
> #include <caml/misc.h>
> }
>
> #define SWIG_CAMLlocal1(x) \
> CAML_VALUE x = 0; \
> CAMLxparam1 (x)
>
> #define SWIG_CAMLlocal2(x, y) \
> CAML_VALUE x = 0, y = 0; \
> CAMLxparam2 (x, y)
>
> #define SWIG_CAMLlocal3(x, y, z) \
> CAML_VALUE x = 0, y = 0, z = 0; \
> CAMLxparam3 (x, y, z)
>
> #define SWIG_CAMLlocal4(x, y, z, t) \
> CAML_VALUE x = 0, y = 0, z = 0, t = 0; \
> CAMLxparam4 (x, y, z, t)
>
> #define SWIG_CAMLlocal5(x, y, z, t, u) \
> CAML_VALUE x = 0, y = 0, z = 0, t = 0, u = 0; \
> CAMLxparam5 (x, y, z, t, u)
>
> #define SWIG_CAMLlocalN(x, size) \
> CAML_VALUE x [(size)] = { 0, /* 0, 0, ... */ }; \
> CAMLxparamN (x, (size))
>
>
>
> /* Declarations for custom block operations. */
>
> /* For more information of Objective Caml custom blocks,
> * consult the Objective Caml manual, section 18.9. */
>
> static struct custom_operations custom_swigtype_ocaml_operations = {
> "org.homelinux.yziquel.ocaml.swig",
> custom_finalize_default,
> custom_compare_default,
> custom_hash_default,
> custom_serialize_default,
> custom_deserialize_default
> };
>
>
>
> #define SWIGVERSION 0x010340
> #define SWIG_VERSION SWIGVERSION
>
>
> #define SWIG_as_voidptr(a) (void *)((const void *)(a))
> #define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),(void**)(a))
>
>
> #include "freeling.h"
>
>
> #include <stdexcept>
>
>
> #if defined(__GNUC__)
> # if __GNUC__ == 2 && __GNUC_MINOR <= 96
> # define SWIG_STD_NOMODERN_STL
> # endif
> #endif
>
>
> #include <string>
> #include <stdexcept>
>
>
> #include <string>
>
>
> #include <list>
>
> extern "C" CAML_VALUE _wrap_new_tokenizer (CAML_VALUE ocaml_arg1)
> {
> CAMLparam0();
> SWIG_CAMLlocal1(caml_result);
> std::string *arg1 = 0 ;
> CAMLxparam1(ocaml_arg1);
> tokenizer *result = 0 ;
>
> {
> std::string arg1_str(String_val(ocaml_arg1), caml_string_length(ocaml_arg1));
> arg1 = &arg1_str;
> }
> result = (tokenizer *)new tokenizer((std::string const &)*arg1);
> {
> caml_result = caml_alloc_custom(&custom_swigtype_ocaml_operations, sizeof (void *), 0, 1);
> *((void **) Data_custom_val(caml_result)) = (void *)result;
> }
> CAMLreturn(caml_result);
> }
>
> extern "C" CAML_VALUE _wrap_tokenizer_tokenize (CAML_VALUE ocaml_arg1, CAML_VALUE ocaml_arg2)
> {
> CAMLparam0();
> SWIG_CAMLlocal1(caml_result);
> tokenizer *arg1 = (tokenizer *) 0 ;
> std::string *arg2 = 0 ;
> CAMLxparam1(ocaml_arg1);
> CAMLxparam1(ocaml_arg2);
> std::list< word > result;
>
> arg1 = *((tokenizer * *) Data_custom_val(ocaml_arg1));
> {
> std::string arg2_str(String_val(ocaml_arg2), caml_string_length(ocaml_arg2));
> arg2 = &arg2_str;
> }
> result = (arg1)->tokenize((std::string const &)*arg2);
> {
> caml_result = caml_alloc_custom(&custom_swigtype_ocaml_operations, sizeof (void *), 0 ,1);
> *((void **) Data_custom_val(caml_result)) = new std::list< word >((const std::list< word > &)result);
> }
> CAMLreturn(caml_result);
> }
>
--
Guillaume Yziquel
http://yziquel.homelinux.org/
prev parent reply other threads:[~2010-02-08 12:38 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-02-08 2:13 Luca de Alfaro
2010-02-08 4:06 ` Michael Ekstrand
2010-02-08 4:17 ` Michael Ekstrand
2010-02-08 16:03 ` [Caml-list] " Luca de Alfaro
2010-02-08 16:33 ` Luca de Alfaro
2010-02-08 17:44 ` Guillaume Yziquel
2010-02-08 17:57 ` Luca de Alfaro
2010-02-09 6:53 ` Basile STARYNKEVITCH
2010-02-08 17:54 ` Guillaume Yziquel
2010-02-09 0:25 ` Michael Ekstrand
2010-02-08 12:38 ` Guillaume Yziquel [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4B7005C6.6030201@citycable.ch \
--to=guillaume.yziquel@citycable.ch \
--cc=caml-list@inria.fr \
--cc=luca@dealfaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox