Version française
Home     About     Download     Resources     Contact us    
Browse thread
How to wrap around C++?
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Guillaume Yziquel <guillaume.yziquel@c...>
Subject: Re: [Caml-list] How to wrap around C++?
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/