The comment of Dynlink.loadfile
says:
No facilities are provided to
access value names defined by the unit. Therefore, the unit
must register itself its entry points with the main program,
e.g. by modifying tables of functions.
The loader program cannot access values of dyn-loaded module without any hint, since it has no idea what values are defined in it just from the .cmo
file. The dynamic loaded module must register its entry point to some state defined in the loader program.
Here is such a minimal example. First, the module for the entry points:
(* entry.ml *)
let f : (unit -> unit) ref = ref (fun () -> assert false)
The loader program:
(* loader.ml *)
let () =
Dynlink.loadfile "plugin.cmo";
!Entry.f ()
The plugin to be dyn-loaded:
(* plugin.ml *)
let () = Entry.f := (fun () -> prerr_endline "hello world")
Here, Plugin
registers its function to Entry.f
which is statically linked to Loader
, so that Loader
can access the function.
They must be compiled as follows:
$ ocamlc -o loader.exe dynlink.cma entry.ml loader.ml
$ ocamlc -c plugin.ml
Execution of loader.exe
should demonstrate how the dyn loading works:
$ ./loader.exe
hello world
Note that Entry
and Loader
must be different modules. Otherwise, you get Uninitialized_global
exception at dyn-loading Plugin
. Dyn-loaded modules can only access values in "already initialized modules", and the loader module thinks itself not yet initialized when Dynlink.loadfile
is called, since the entire evaluation of the module is not finished yet.
Entry.f
is the simplest state to have only one entry point. To dyn-load many values, you may want to have more complicated data structure, such as (string, (unit -> unit)) list ref
or (string, (unit -> unit)) Hashtbl.t
.