Running code only once in an OCaml library
I am writing an OCaml library that has some initialization code that needs to be run only once during the lifetime of the program using the library (and store some state that will persist for the lifetime of the program, but only be used within the library itself), and some cleanup code that needs to be run only as the program using the library exits.
If it is relevant, my library is two parts: an interface to a low-level C library, and some higher-level stuff to make programming with it easier. Can I do what I need somewhere in the C? Ideally my users wouldn't care how it was implemented, they would never see the C bits.
In Python I would d开发者_高级运维o this by running code on import
but OCaml's open
doesn't actually run anything, it just sugars the module namespace, and then Python's atexit
, but I can't find the Ocaml equivalent.
One approach I have considered is structuring my library as a "framework" but I don't think it's important enough to warrant such an over-engineered approach. Thanks!
UPDATE: OK got it - I think. I am using the C code to handle the cleanup on exit and I have monkeyed with the code a bit so there is a pointer to the global state on the C side
It would appear that in my library I now have
let global_env = env_create ()
And when it is open
'd by the main program, this does get run... But how?
Note that this can be done on the OCaml side with Pervasives.at_exit
and top-level statements to create the environment and install the cleanup code :
let env = init ()
let cleanup () = do_clean env
let () = at_exit cleanup
let f x = f_stub env x
The toplevel statements are executed when the module is loaded (whether you eventually use it or not) and modules are loaded in the order you specified them at link time (thus modules depending on others are guaranteed that their dependencies are initialized when their turn comes), see "Arguments ending with .cmo" in the manual of ocamlc. This entails that toplevel statements will have executed before you try to access the module. It's not a matter of opening the module, open
is just a (bad) syntactic convenience.
If you want the init code to be performed if and only if a function of the module eventually gets called use a lazy value :
let env = lazy (init ())
let cleanup () = if Lazy.lazy_is_val env then (do_clean env) else ()
let () = at_exit cleanup
let f x = f_stub (Lazy.force env) x
Btw. don't forget to document the resulting issues with thread-safety...
Just as let x = function ...
defines a function x
that is available from that point
onwards, your let global_env = ...
defines a value global_env
that is. If you don't
need the return value of env_create
, because you run it only for it's side effects,
you could also just mention env_create ()
at the end (to be honest, anywhere) of the
ml file.
In this case I would do let _ = env_create ()
though, which I think is more explicit.
EDIT: R pointed out that the following is wrong:
"To do it purely in C, I think that _init
and _fini
are the things to look for."
As explained in this HOWTO, it is indeed deprecated and should now be done via attributes.
精彩评论