jbuckmccready / cavalier_contours

2D polyline/shape library for offsetting, combining, etc.
Apache License 2.0
144 stars 12 forks source link

C FFI Design #2

Open jbuckmccready opened 3 years ago

jbuckmccready commented 3 years ago

This is a WIP for outlining the patterns to be followed for creating the C FFI. The content in this issue will be edited and evolve into the actual top level overview documentation for the C API. I don't have a lot of experience with good C APIs, so ideas are welcome.

Error Handling

Naming and Type Conventions

Option Structs

There is a need to support option parameters for more complex functions. There could be an option struct associated with each function that supports optional parameters, for example: The cavc_pline_eval_offset could have a cavc_pline_eval_offset_o struct as one of the parameters. The cavc_pline_eval_offset_o struct could hold epsilon values and other parameters to be used by the offset algorithm and could look something like this:

pub struct cavc_pline_eval_offset_o
{
    pub pos_equal_eps: f64,
    pub slice_join_eps: f64,
    pub offset_dist_eps: f64,
    pub handle_self_intersects: u8,
}

There could then also be a cavc_pline_eval_offset_o_i function that accepts a *mut cavc_pline_eval_offset_o for initializing sensible defaults.

The issue with this is API stability if we want to add new fields to the option struct in the future. Even if all the functions take the option struct by reference/pointer the functions wont know if a newer field is present or not. And this is really bad considering everything may "seem to work" when in fact the library is reading past the end of the struct memory because the calling side is assuming a different set of fields in the struct.

One solution is to make the option struct opaque and have the construction of the struct and all fields be modified through functions but this has several downsides: a create and free function must be created for the type and memory allocation managed by the caller, all fields must have a get/set function associated with them, and from a performance perspective heap memory allocations and frees must be performed around managing the option parameters (which could be very frequent).

Another solution is to not attempt to make the option structs be forward compatible and just create a new function and new struct type if new options are added (while keeping the old function and struct the same for continued compatibility), e.g. cavc_pline_eval_offset2 and cavc_pline_eval_offset_o2.

Current plan is to follow the pattern of just creating a new function and option struct, leaving the previous version present to not break backward compatibility. However this would all be done after a 1.0 release, before then there may be some breaking changes in iteration to arrive at a 1.0 API and ideally no second version functions are required. It may also be that there is just a 2.0 release if enough changes pile up of this nature.