-
-
Notifications
You must be signed in to change notification settings - Fork 2
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
RFC: Brainstorming pointer parameters #16
base: main
Are you sure you want to change the base?
Conversation
Directly match out an empty list rather than using empty string "" for label.
(Some | ||
(call "Nativeint_val" | ||
[ call "Field" [ var (caml_ ^ name); int 1 ] ])) |
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 value
passed 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))
. 1
because the address field is the second field in the 'a cptr
type.
{|#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)); | ||
} |} |
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.
I ended up needing this to handle the creation of pointers. This is effectively replacing what we do inside the of_value
functions. In my example its just for an int
but it could also be for struct
s.
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 (doggo -> unit
) and it mallocs, or it stack allocates it on the C side a.la #1 (comment)
@@ -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"; |
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.
I like this idea more and more. This can be its own lil one-liner PR :)
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 *) |
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.
I haven't fleshed this out yet.
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; |
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.
ocaml garbage collector handles freeing it up when its no longer used. This will need to be customisable hence the lifetime
field.
// 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"); | ||
} No newline at end of file | ||
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.
Commented out old code to focus only on the current brainstorming.
include Doggo_sys | ||
(** Here is where we would write our wrappers around the raw bindings *) | ||
|
||
let wrapper_print_age (age : int) = print_age ~age:(create_ptr age) |
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 doesn't actually set the value yet... trying to think how to make that ergonomic, ideally without going through another FFI call.
Motivated by the need to be able to pass strings but only working back from a header we have
char*
.here is a start on what we might need to have.
looking for feedback and ideas!