-
-
Notifications
You must be signed in to change notification settings - Fork 2
RFC: Brainstorming pointer parameters #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0732f2e
d832014
292906a
66affc7
725e130
89bc864
44d7a3a
223ab37
8402835
3ae7613
f7f90ad
82d651c
701106b
4fb05b5
dee97d7
eda74d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,8 @@ let with_file file fn = | |
| close_out oc; | ||
| () | ||
|
|
||
| let read_file file = In_channel.with_open_bin file In_channel.input_all | ||
|
|
||
| let write_dune_file (dune : Dunefile.t) = | ||
| with_file "dune" @@ fun fmt -> | ||
| Format.fprintf fmt | ||
|
|
@@ -22,11 +24,35 @@ let write_dune_file (dune : Dunefile.t) = | |
| Format.fprintf fmt "\n%!" | ||
|
|
||
| let write_caml_files caml (dune : Dunefile.t) = | ||
| let runtime_ocaml = read_file "../bindgen/runtime.ml" in | ||
| with_file dune.caml_file_name @@ fun fmt -> | ||
| Format.fprintf fmt "(* automatically generated by ocaml-bindgen 0.0.1 *)\n"; | ||
| Format.fprintf fmt "%s\n" runtime_ocaml; | ||
| Format.fprintf fmt "%s\n%!" (Format.asprintf "%a" Pprintast.structure caml) | ||
|
|
||
| let write_c_files (c : C.program) (dune : Dunefile.t) = | ||
| with_file dune.c_file_name @@ fun fmt -> | ||
| Format.fprintf fmt "/* automatically generated by ocaml-bindgen 0.0.1 */\n"; | ||
| Format.fprintf fmt "%s\n%!" (Format.asprintf "%a" C.pp c) | ||
| Format.fprintf fmt "\n%s\n%!" (Format.asprintf "%a" C.pp c); | ||
| Format.fprintf fmt | ||
| {|#include <stdlib.h> | ||
| value bindgen_alloc(value caml_size) { | ||
| CAMLparam1(caml_size); | ||
|
|
||
| // Convert OCaml integer to C size | ||
| size_t size = Int_val(caml_size); | ||
| printf("Allocated size %%ld \n", size); | ||
|
|
||
| void* ptr = malloc(sizeof(size)); | ||
| if (ptr == NULL) { | ||
| // TODO: handle allocation failure | ||
| CAMLreturn(Val_unit); | ||
| } | ||
|
|
||
| // Wrap the pointer as an OCaml value | ||
| CAMLreturn(caml_copy_nativeint(ptr)); | ||
| } | ||
|
|
||
| void bindgen_free(value caml_addr) { | ||
| free(Nativeint_val(caml_addr)); | ||
| } |} | ||
|
Comment on lines
+38
to
+58
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I ended up needing this to handle the creation of pointers. This is effectively replacing what we do inside the I can see it being a bit slower, but there might be a way to specialise certain cases to keep the current behaviour, wherein you pass a record by value ( |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,6 @@ type t = { lib_name : string; caml_file_name : string; c_file_name : string } | |
| let from_ir (ir : Ir.t) : t = | ||
| { | ||
| lib_name = ir.lib_name; | ||
| caml_file_name = ir.lib_name ^ ".ml"; | ||
| caml_file_name = ir.lib_name ^ "_sys" ^ ".ml"; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this idea more and more. This can be its own lil one-liner PR :) |
||
| c_file_name = "caml_" ^ ir.lib_name ^ ".c"; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| type lifetime = | ||
| | Function | ||
| (** The value can live for the lifetime of the function call, which upon return will signal that the | ||
| value can be dropped (finalizer?) *) | ||
| | Ocaml (** The value is managed by the OCaml runtime *) | ||
| | C | ||
| (** The value is allocated and passed to C which is then in charge of cleaning it up *) | ||
|
Comment on lines
+1
to
+7
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't fleshed this out yet. |
||
|
|
||
| type 'a cptr = { lifetime : lifetime; addr : nativeint } | ||
|
|
||
| external bindgen_alloc : size:int -> nativeint = "bindgen_alloc" | ||
| external bindgen_free : nativeint -> unit = "bindgen_free" | ||
|
|
||
| let sizeof _ = 4 (* TODO: how to handle different types? *) | ||
|
|
||
| let create_ptr (value : 'a) : 'a cptr = | ||
| let addr = bindgen_alloc ~size:(sizeof value) in | ||
| print_endline ("Addr: " ^ Nativeint.to_string addr); | ||
| Gc.finalise bindgen_free addr; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ocaml garbage collector handles freeing it up when its no longer used. This will need to be customisable hence the |
||
| { lifetime = Ocaml; addr } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,40 +1,38 @@ | ||
| /* automatically generated by ocaml-bindgen 0.0.1 */ | ||
|
|
||
| #include "doggo.h" | ||
| #include <caml/alloc.h> | ||
| #include <caml/callback.h> | ||
| #include <caml/fail.h> | ||
| #include <caml/memory.h> | ||
| #include <caml/mlvalues.h> | ||
| #include <caml/unixsupport.h> | ||
| Doggo* caml_Doggo_of_value(value caml_x) { | ||
| Doggo* x = malloc(sizeof(struct Doggo)); | ||
| x->many = Int_val(Field(caml_x, 0)); | ||
| x->breed = Int_val(Field(caml_x, 1)); | ||
| x->wow = Int_val(Field(caml_x, 2)); | ||
| return x; | ||
| void caml_print_age(value caml_age) { | ||
| CAMLparam1(caml_age); | ||
| int* age = Nativeint_val(Field(caml_age, 1)); | ||
| print_age(age); | ||
| CAMLreturn0; | ||
| } | ||
|
|
||
| value caml_Doggo_to_value(struct Doggo* x) { | ||
| CAMLparam0(); | ||
| CAMLlocal1(caml_x); | ||
| caml_x = caml_alloc_tuple(3); | ||
| Store_field(caml_x, 0, Val_int(x->many)); | ||
| Store_field(caml_x, 1, Val_int(x->breed)); | ||
| Store_field(caml_x, 2, Val_int(x->wow)); | ||
| CAMLreturn(caml_x); | ||
| } | ||
|
|
||
| void caml_eleven_out_of_ten_majestic_af(value caml_pupper) { | ||
| CAMLparam1(caml_pupper); | ||
| Doggo* pupper = caml_Doggo_of_value(caml_pupper); | ||
| eleven_out_of_ten_majestic_af(pupper); | ||
| CAMLreturn0; | ||
| } | ||
| #include <stdlib.h> | ||
| value bindgen_alloc(value caml_size) { | ||
| CAMLparam1(caml_size); | ||
|
|
||
| void caml_no_input_no_output() { | ||
| CAMLparam0(); | ||
| no_input_no_output(); | ||
| CAMLreturn0; | ||
| } | ||
| // Convert OCaml integer to C size | ||
| size_t size = Int_val(caml_size); | ||
| printf("Allocated size %ld \n", size); | ||
|
|
||
| void* ptr = malloc(sizeof(size)); | ||
| if (ptr == NULL) { | ||
| // TODO: handle allocation failure | ||
| CAMLreturn(Val_unit); | ||
| } | ||
|
|
||
| // Wrap the pointer as an OCaml value | ||
| CAMLreturn(caml_copy_nativeint(ptr)); | ||
| } | ||
|
|
||
| void bindgen_free(value caml_addr) { | ||
| free(Nativeint_val(caml_addr)); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,30 @@ | ||
| #include <stdio.h> | ||
| #include "doggo.h" | ||
|
|
||
| static const char* BreedToString[4] = { | ||
| "Labrador", | ||
| "Golden Retriever", | ||
| "Pug", | ||
| "Poodle" | ||
| }; | ||
| // static const char* BreedToString[4] = { | ||
| // "Labrador", | ||
| // "Golden Retriever", | ||
| // "Pug", | ||
| // "Poodle" | ||
| // }; | ||
|
|
||
| void eleven_out_of_ten_majestic_af(Doggo* pupper) { | ||
| printf("doggo says %d\n", pupper->many); | ||
| printf("doggo is a %s\n", BreedToString[pupper->breed]); | ||
| } | ||
| // void eleven_out_of_ten_majestic_af(Doggo* pupper) { | ||
| // printf("doggo says %d\n", pupper->many); | ||
| // printf("doggo is a %s\n", BreedToString[pupper->breed]); | ||
| // printf("doggo weighs %.1fkg\n", pupper->weight); | ||
| // } | ||
|
|
||
| // void no_input_no_output(void) { | ||
| // printf("We are doing nothing (of importance)\n"); | ||
| // } | ||
|
|
||
| void no_input_no_output(void) { | ||
| printf("We are doing nothing (of importance)\n"); | ||
| } | ||
| void print_age(int* age) { | ||
| printf("Age: %d\n", *age); | ||
| } | ||
| // void print_name(char* name) { | ||
| // printf("Name: %s\n", name); | ||
| // } | ||
| // void print_doggo(Doggo *dog) { | ||
| // printf("wow: %c\n", dog->wow); | ||
| // print_age(&dog->many); | ||
| // } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is where we get the pointer out from the
valuepassed to the glue function.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It becomes
Nativeint_val(Field(param_name, 1)).1because the address field is the second field in the'a cptrtype.