Est-il possible d’appeler des fonctions C depuis OCaml, sans types encapsulés?

J’essaie d’écrire un compilateur (désespérément minuscule, simple – je n’ai aucune idée de ce que je fais, soyons réel!) Dans OCaml.

Je voudrais vraiment éviter d’insérer un code ISO-C dans le projet (même si je connais assez bien le langage C; l’objective ici est d’apprendre et d’utiliser exclusivement OCaml.) En conséquence, je dois écrire un «runtime» pour le programme OCaml, comstackz-le séparément du projet principal, puis reliez-le à la sortie du compilateur lui-même.

Malheureusement, cela ressemble à n’importe quelle fonction externe – même celles qui ne touchent aucune structure de données OCaml / le tas OCaml, devraient être construites à l’aide des macros C d’OCaml:

CAMLprim value scheme_entry(value unit) { int i; i = 42; return Val_int(i); } 

Ce n’est probablement pas une option, si j’émets moi-même les instructions de assembly. (Du moins, pas avant que j’apprenne un peu plus!)

Existe-t-il un moyen (y compris les hacky – il s’agit d’un projet d’apprentissage personnel) pour invoquer des fonctions extrêmement simples comme celles ci-dessous de OCaml?

 _scheme_entry: movl $42 %eax ret 

Pour référence, je travaille à travers l’IACC de Ghuloum: http://ell.io/tt$ocameel

    Malheureusement, cela ressemble à n’importe quelle fonction externe – même celles qui ne touchent aucune structure de données OCaml / le tas OCaml, devraient être construites à l’aide des macros C d’OCaml:

    Non ils ne sont pas. Si votre fonction ne touche pas du tout à OCaml, ou n’alloue pas ou ne fonctionne pas avec les valeurs allouées, vous pouvez appeler des fonctions directement, par exemple,

     value scheme_entry(value unit) { int i; i = 42; return Val_int(i); } 

    et côté OCaml:

     external scheme_entry : unit -> int = "scheme_entry" [@@noalloc] 

    Dans les dernières versions d’OCaml, nous avons eu encore plus de contrôle, de sorte que nous puissions passer des flottants et des entiers plus grands sans boxing / unboxing. Lisez ici pour plus d’informations.

    Notez que Val_int est simplement une macro qui décale un entier C d’un bit vers la gauche et définit le bit le moins significatif sur un, c’est-à-dire que (((unsigned)(x) << 1)) + 1) . Ainsi, si vous n'avez pas besoin de traduire les entiers OCaml en entiers C et vice-versa, vous n'avez même pas besoin d'utiliser ces macros. Par conséquent, votre environnement d'exécution peut ne pas être au courant de OCaml, par exemple:

      void runtime_init(void) { printf("Hello from runtime"); } 

    et côté OCaml:

      val runtime_init : unit -> unit = "runtime_init" [@@noalloc] 

    Remarque: [@@noalloc] atsortingbut [@@noalloc] été ajouté dans OCaml 4.03. Avant cette version, vous devez utiliser "noalloc" , par exemple,

      val runtime_init : unit -> unit = "runtime_init" "noalloc" 

    Bien entendu, votre fonction extrêmement simple doit obéir à la convention d'appel C qui convient à votre architecture et à votre système d'exploitation. Donc, si votre fonction détruit certains registres qui doivent être préservés, vous devez vous attendre à des problèmes, par exemple, sur amd64 ABI, vous devez conserver rbp, rbx, r12-r15. Il n’ya rien d’OCaml spécifique à cette exigence, juste une convention d’appel C normale.

    Vous voudrez peut-être utiliser la bibliothèque ctypes et les packages associés, disponibles via OPAM.

     opam install ctypes ctypes-foreign posix-types 

    Exemple:

     open Foreign open Ctypes open Posix_types let getpid = foreign "getpid" (void @-> returning pid_t) let () = Printf.printf "%d\n" (Pid.to_int (getpid ())) 

    (La ctypes nécessite les packages ctypes , ctypes.foreign et posix-types .)