Skip to content

Commit

Permalink
[#210] Better naming convention for C API
Browse files Browse the repository at this point in the history
  • Loading branch information
elBoberido committed Jul 12, 2024
1 parent ae4396f commit 619a3a4
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 101 deletions.
47 changes: 32 additions & 15 deletions iceoryx2-ffi/ffi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,35 @@

- all constructs start with `iox2_`
- `structs` end with a `_t`
- mutable handles end with a `_mut_h` and are a type definition to a `*mut iox2_foo_storage_t`
- immutable handles end with a `_h` and are a type definition to a `*const iox2_foo_storage_internal_t` which holds the Rust type `Option<Foo>`
- handles end with a `_h` and are a type definition to a `struct iox2_foo_h_t;` as `pub type iox2_foo_h = *mut iox2_foo_h_t`
- immutable pointer to the Rust type end with a `_ptr` and are a type definition to a `struct iox2_foo_ptr_t;` as `pub type iox2_foo_ptr = *mut iox2_foo_ptr_t`
- mutable pointer to the Rust type end with a `_mut_ptr` and are a type definition to a `struct iox2_foo_mut_ptr_t;` as `pub type iox2_foo_mut_ptr = *mut iox2_foo_mut_ptr_t`
- `enums` ends with a `_e`

# Pattern for Type Erasure

The type erasure is usually done in two stages with `iox2_foo_storage_internal_t` and `iox2_foo_storage_t`.
The type erasure is usually done in two stages with `iox2_foo_storage_t` and `iox2_foo_t`.

The `iox2_foo_storage_internal_t` is the storage for the Rust type `Option<Foo>` and must match the size and alignment of `Option<Foo>`.
The `iox2_foo_storage_t` is the storage for the Rust type `Option<Foo>` and must match the size and alignment of `Option<Foo>`.
If the internal storage must hold multiple types, the size and alignment is respectively the max value of the types.
The struct is not supposed to be used standalone but always in combination with an `iox2_foo_storage_t`.
The struct is not supposed to be used standalone but always in combination with an `iox2_foo_t`.
Assuming the size is 160 and the alignment is 8, then the storage is defined as following
```rs
#[repr(C)]
#[repr(align(8))] // alignment of Option<Foo>
pub struct iox2_foo_storage_internal_t {
pub struct iox2_foo_storage_t {
internal: [u8; 160], // magic number obtained with size_of::<Option<Foo>>()
}
```

The `iox2_foo_storage_t` is the actual storage that is used by the user. It contains the internal storage, a deleter and
optionally further data, e.g. to distinguish between multiple allowed types of `iox2_foo_storage_internal_t`.
The `iox2_foo_t` is the actual type that is used by the user. It contains the internal storage, a deleter and
optionally further data, e.g. to distinguish between multiple allowed types of `iox2_foo_storage_t`.
```rs
#[repr(C)]
pub struct iox2_foo_storage_t {
internal: iox2_foo_storage_internal_t,
deleter: fn(*mut iox2_foo_storage_t),
pub struct iox2_foo_t {
/// cbindgen:rename=internal
foo: iox2_foo_storage_t,
deleter: fn(*mut iox2_foo_t),
}
```

Expand All @@ -38,10 +40,25 @@ corresponding `iox2_foo_drop` shall be used to destruct the underlying Rust type
If the Rust API takes the ownership of `Foo`, the C API will also take the ownership of the handle and `iox2_foo_drop` shall not be
called.

The corresponding handles are defined like this
When the handle is passed to a function, the ownership of the underlying data is moved to that specific function and the `*_h` handles
as well as all the `*_ptr` related to that handle are invalid. Accessing the handles or pointer afterwards lead to undefined behavior.
The only exception are the `iox2_cast_*` functions which can be used to get `_ptr` and `_mut_ptr` pointer the the Rust type.

The corresponding handle and pointer are defined like this
```rs
pub type iox2_foo_mut_h = *mut iox2_foo_storage_t;
pub type iox2_foo_h = *const iox2_foo_storage_internal_t;
#[repr(C)]
pub struct iox2_foo_h_t;
pub type iox2_foo_h = *mut iox2_foo_h_t;

#[repr(C)]
pub struct iox2_foo_ptr_t;
pub type iox2_foo_ptr = *const iox2_foo_ptr_t;

#[repr(C)]
pub struct iox2_foo_mut_ptr_t;
pub type iox2_foo_mut_ptr = *mut iox2_foo_mut_ptr_t;
```

The `_mut_h` handle is in general created by a builder and the `_h` handle is in general provided by a function, e.g. as return value.
The `_h` handle is in general created by a builder and the `_ptr` pointer ar in general provided by a function, e.g. as return value.

The `src/node_name.rs` file can be used as a more comprehensive example on how to implement an FFI binding for a specific type.
14 changes: 7 additions & 7 deletions iceoryx2-ffi/ffi/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#![allow(non_camel_case_types)]

use crate::{
iox2_callback_progression_e, iox2_config_h, iox2_node_name_h, iox2_service_builder_mut_h,
iox2_callback_progression_e, iox2_config_h, iox2_node_name_ptr, iox2_service_builder_mut_h,
iox2_service_builder_storage_t, iox2_service_name_mut_h, iox2_service_type_e, IntoCInt,
IOX2_OK,
};
Expand Down Expand Up @@ -129,15 +129,15 @@ pub type iox2_node_list_callback_context = *mut c_void;
///
/// * [`iox2_node_state_e`]
/// * [`iox2_node_id_h`]
/// * [`iox2_node_name_h`](crate::iox2_node_name_h) -> `NULL` for `iox2_node_state_e::INACCESSIBLE` and `iox2_node_state_e::UNDEFINED`
/// * [`iox2_node_name_ptr`](crate::iox2_node_name_ptr) -> `NULL` for `iox2_node_state_e::INACCESSIBLE` and `iox2_node_state_e::UNDEFINED`
/// * [`iox2_config_h`](crate::iox2_config_h) -> `NULL` for `iox2_node_state_e::INACCESSIBLE` and `iox2_node_state_e::UNDEFINED`
/// * [`iox2_node_list_callback_context`] -> provided by the user to [`iox2_node_list`] and can be `NULL`
///
/// Returns a [`iox2_callback_progression_e`](crate::iox2_callback_progression_e)
pub type iox2_node_list_callback = extern "C" fn(
iox2_node_state_e,
iox2_node_id_h,
iox2_node_name_h,
iox2_node_name_ptr,
iox2_config_h,
iox2_node_list_callback_context,
) -> iox2_callback_progression_e;
Expand All @@ -146,13 +146,13 @@ pub type iox2_node_list_callback = extern "C" fn(

// BEGIN C API

/// Returns the [`iox2_node_name_h`](crate::iox2_node_name_h), an immutable handle to the node name.
/// Returns the [`iox2_node_name_ptr`](crate::iox2_node_name_ptr), an immutable pointer to the node name.
///
/// # Safety
///
/// The `node_handle` must be valid and obtained by [`iox2_node_builder_create`](crate::iox2_node_builder_create)!
#[no_mangle]
pub unsafe extern "C" fn iox2_node_name(node_handle: iox2_node_mut_h) -> iox2_node_name_h {
pub unsafe extern "C" fn iox2_node_name(node_handle: iox2_node_mut_h) -> iox2_node_name_ptr {
debug_assert!(!node_handle.is_null());

unsafe {
Expand Down Expand Up @@ -264,7 +264,7 @@ fn iox2_node_list_impl<S: Service>(
}
}

/// Calls the callback repeatedly with an [`iox2_node_state_e`], [`iox2_node_id_h`], [´iox2_node_name_h´] and [`iox2_config_h`] for
/// Calls the callback repeatedly with an [`iox2_node_state_e`], [`iox2_node_id_h`], [´iox2_node_name_ptr´] and [`iox2_config_h`] for
/// all [`Node`](iceoryx2::node::Node)s in the system under a given [`Config`](iceoryx2::config::Config).
///
/// # Arguments
Expand Down Expand Up @@ -453,7 +453,7 @@ mod test {
extern "C" fn node_list_callback(
node_state: iox2_node_state_e,
_node_id: iox2_node_id_h,
_node_name: iox2_node_name_h,
_node_name: iox2_node_name_ptr,
_config: iox2_config_h,
ctx: iox2_node_list_callback_context,
) -> iox2_callback_progression_e {
Expand Down
22 changes: 17 additions & 5 deletions iceoryx2-ffi/ffi/src/node_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#![allow(non_camel_case_types)]

use crate::{
iox2_node_mut_h, iox2_node_name_drop, iox2_node_name_mut_h, iox2_node_storage_t,
iox2_node_mut_h, iox2_node_name_drop, iox2_node_name_h, iox2_node_name_t, iox2_node_storage_t,
iox2_service_type_e, IntoCInt, IOX2_OK,
};

Expand Down Expand Up @@ -142,16 +142,28 @@ pub unsafe extern "C" fn iox2_node_builder_new(
handle
}

/// Sets the node name for the builder
///
/// # Arguments
///
/// * `node_builder_handle` - Must be a valid [`iox2_node_builder_mut_h`] obtained by [`iox2_node_builder_new`].
/// * `node_name_handle` - Must be a valid [`iox2_node_name_h`] obtained by [`iox2_node_name_new`](crate::iox2_node_name_new).
///
/// Returns IOX2_OK
///
/// # Safety
///
/// `node_builder_handle` as well as `node_name_handle` must be valid handles
#[no_mangle]
pub unsafe extern "C" fn iox2_node_builder_set_name(
node_builder_handle: iox2_node_builder_mut_h,
node_name_handle_mut: iox2_node_name_mut_h,
node_name_handle: iox2_node_name_h,
) -> c_int {
debug_assert!(!node_builder_handle.is_null());
debug_assert!(!node_name_handle_mut.is_null());
debug_assert!(!node_name_handle.is_null());

let node_name = unsafe { (*node_name_handle_mut).take().unwrap() };
iox2_node_name_drop(node_name_handle_mut);
let node_name = unsafe { (*iox2_node_name_t::cast(node_name_handle)).take().unwrap() };
iox2_node_name_drop(node_name_handle);

let node_builder = std::mem::take(unsafe { (*node_builder_handle).node_builder_assume_init() });
let node_builder = node_builder.name(node_name);
Expand Down
Loading

0 comments on commit 619a3a4

Please sign in to comment.