Skip to content
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

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

omnisci3nce
Copy link
Contributor

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!

Comment on lines +195 to +197
(Some
(call "Nativeint_val"
[ call "Field" [ var (caml_ ^ name); int 1 ] ]))
Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Comment on lines +38 to +58
{|#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));
} |}
Copy link
Contributor Author

@omnisci3nce omnisci3nce Mar 16, 2024

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 structs.

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";
Copy link
Contributor Author

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 :)

Comment on lines +1 to +7
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 *)
Copy link
Contributor Author

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;
Copy link
Contributor Author

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.

Comment on lines 9 to 30
// 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);
// }
Copy link
Contributor Author

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)
Copy link
Contributor Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant