From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail1-relais-roc.national.inria.fr (mail1-relais-roc.national.inria.fr [192.134.164.82]) by walapai.inria.fr (8.13.6/8.13.6) with ESMTP id q448i2Hk031328 for ; Fri, 4 May 2012 10:44:02 +0200 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AqgBAHaWo0/ZSMDjkWdsb2JhbABFswgiAQEBAQkLCwcUAySCCQEBBAEnEz8FCwshJQ8BBA0bIROHfgEDBgkHsFMfKw2JU4oLhwIElw+EUoVch20 X-IronPort-AV: E=Sophos;i="4.75,530,1330902000"; d="scan'208";a="156721493" Received: from fmmailgate02.web.de ([217.72.192.227]) by mail1-smtp-roc.national.inria.fr with ESMTP; 04 May 2012 10:43:57 +0200 Received: from moweb002.kundenserver.de (moweb002.kundenserver.de [172.19.20.108]) by fmmailgate02.web.de (Postfix) with ESMTP id 17EAA1C426579 for ; Fri, 4 May 2012 10:43:56 +0200 (CEST) Received: from frosties.localnet ([95.208.118.96]) by smtp.web.de (mrweb002) with ESMTPA (Nemesis) id 0MJTHn-1SRbpA3AjL-0032rh; Fri, 04 May 2012 10:43:55 +0200 Received: from mrvn by frosties.localnet with local (Exim 4.77) (envelope-from ) id 1SQE7H-0000v9-9D; Fri, 04 May 2012 10:43:55 +0200 From: Goswin von Brederlow To: Joel Reymont Cc: Gabriel Scherer , Goswin von Brederlow , caml-list@inria.fr References: <871un1z8sq.fsf@frosties.localnet> Date: Fri, 04 May 2012 10:43:55 +0200 In-Reply-To: (Joel Reymont's message of "Thu, 3 May 2012 19:41:27 +0100") Message-ID: <8762ccqqt0.fsf@frosties.localnet> User-Agent: Gnus/5.110009 (No Gnus v0.9) XEmacs/21.4.22 (linux, no MULE) MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Provags-ID: V02:K0:MhudnK7C7VaznWSV1UCg1Auim/Vu5Qikmgp/se36XEN YB4zjy2PiPYXCTwgJMT1KnJNkS3SpkOoDUtKKV0Gco5fXyQiw1 nRIH/d+8cz4oTLDViy4XgHTDAkhHLurcEvOjcc4A4gUoK5elAq 1IYrjD5uKT2MBdj/52OgP2uBxo/v3Fr8wOuL4Ezm39LsCAZOI/ BUQCqB6LmTl964KU+/KkQ== Subject: Re: [Caml-list] using modules to wrap c++ classes Joel Reymont writes: > So I ended up with something like this > > --- Callbacks.ml > > type t > > type 'a callback = > | Alert of ('a -> Alert.t -> unit) > | AskQuote of ('a -> Ask.t -> unit) > | BestAskQuote of ('a -> Ask.t -> unit) > | BidQuote of ('a -> Bid.t -> unit) > | BestBidQuote of ('a -> Bid.t -> unit) > | ClosePrice of ('a -> ClosePrice.t -> unit) > | ClosingIndicator of ('a -> ClosingIndicator.t -> unit) > | EndQuote of of ('a -> EndQuote.t -> unit) > | EquityOptionList of of ('a -> EquityOptionList.t -> unit) > | EquityOptionStrategyList of of ('a -> EquityOptionStrategyList.t -> unit) > | HighPrice of ('a -> HighPrice.t -> unit) > | Indicator of ('a -> Indicator.t -> unit) > | IndicatorReplay of ('a -> IndicatorReplay.t -> unit) > | LimitOrderBook of ('a -> LimitOrderBook.t -> unit) > | LowPrice of ('a -> LowPrice.t -> unit) > | MarketMode of ('a -> MarketMode.t -> unit) > | OpenPrice of ('a -> OpenPrice.t -> unit) > ... > > external make : unit -> t = "Callbacks_new" > external set : t -> 'a callback -> unit = "Callbacks_set" > > I now want to grab the closure from the constructor on the C side and > stick it into a callback array. Is this sufficient? > > extern "C" CAMLprim value > Callbacks_set(value v) > { > CAMLparam1(v); > int i = Tag_val(v); > CAMLlocal1(f); > f = Field(0, v); > caml_register_global_root(f); > callbacks[i] = f; > CAMLreturn(Val_unit); > } > > Thanks, Joel This won't work. The value f is a pointer to a closure block butr that block is under the control of the GC. It will be moved around during compaction and then your callbacks[i] is invalid. As discussed on irc you need to create your callbacks[] array as ocaml block and register that itself as root. That also has the benefit that you only have to register a single root instead of 50. For an example you can look at my libfuse bindings: http://git.ocamlcore.org/cgi-bin/gitweb.cgi?p=libfuse-ocaml/libfuse-ocaml.git;a=tree;f=lib;h=72f44b5090e0d1341d679f52249bbf3974709c1c;hb=HEAD The idea there is to have a ocaml block with callbacks and a custom block with finalizer that contains a pointer to the ocaml block mixed with plain C data. The GC will not inspect the contents of the custom block so the block of callbacks is registered as global root to keep it alive and unregistered in the finalizer. Since you are going to use a static block of calbacks you only need to register the root, it never dies. In my case I use a record containing all callbacks to initialize the C side. With fuse most use cases will set most of the callbacks so having to set each in turn would be more work. I also use Obj.magic 0 to initialize callbacks that are unused. An alternative is to use 'a option types but I decided against that to avoid the extra indirection. Those callbacks get called a lot and speed is relevant. Callbacks can be set as needed and the intended use for this is let my_ops = { default_ops with init = my_init; destroy = my_destroy; } Using the "with" syntx of records one only overrides the callbckas one wishes to use. Changing the code to set callbacks individualy is left as an exercise to the reader. :) MfG Goswin