Deriving ReprC
for custom structs
Usage
use ::safer_ffi::prelude::*;
#[derive_ReprC] // <- `::safer_ffi`'s attribute
#[repr(C)] // <- defined C layout is mandatory!
pub
struct Point {
x: i32,
y: i32,
}
#[ffi_export]
fn get_origin ()
-> Point
{
Point { x: 0, y: 0 }
}
Generated C header
typedef struct Point {
int32_t x;
int32_t y;
} Point_t;
Point_t get_origin (void);
Usage with Generic Structs
#[derive_ReprC]
supports generic structs:
use ::safer_ffi::prelude::*;
/// The struct can be generic...
#[derive_ReprC]
#[repr(C)]
pub
struct Point<Coordinate> {
x: Coordinate,
y: Coordinate,
}
/// ... but its usage within an `#[ffi_export]`-ed function must
/// no longer be generic (it must have been instanced with a concrete type)
#[ffi_export]
fn get_origin ()
-> Point<i32>
{
Point { x: 0, y: 0 }
}
Generated C header
Each monomorphization leads to its own C definition:
-
Point<i32>
typedef struct { int32_t x; int32_t y; } Point_int32_t;
-
Point<f64>
typedef struct { double x; double y; } Point_double_t;
Requirements
-
All the fields must be
ReprC
or generic. -
The struct must be non-empty (because ANSI C does not support empty structs)
Opaque types (forward declarations)
Sometimes you may be dealing with a complex Rust type and you don't want to go
through the hassle of recusrively changing each field to make it ReprC
.
In that case, the type can be defined as an opaque object w.r.t. the C API, which will make it usable by C but only through a layer of pointer indirection and function abstraction:
#[derive_ReprC]
#[repr(opaque)] // <-- instead of `#[repr(C)]`
pub
struct ComplicatedStruct {
path: PathBuf,
cb: Rc<dyn 'static + Fn(&'_ Path)>,
x: i32,
}
Only braced struct definitions are currently supported. Opaque tuple structs and
enum
s ought to supported soon.
Example
use ::std::{
path::{Path, PathBuf},
rc::Rc,
};
use ::safer_ffi::prelude::*;
#[derive_ReprC]
#[repr(opaque)]
pub
struct ComplicatedStruct {
path: PathBuf,
cb: Rc<dyn 'static + Fn(&'_ Path)>,
x: i32,
}
#[ffi_export]
fn create ()
-> repr_c::Box<ComplicatedStruct>
{
Box::new(ComplicatedStruct {
path: "/tmp".into(),
cb: Rc::new(|path| println!("path = `{}`", path.to_string_lossy())),
x: 42,
}).into()
}
#[ffi_export]
fn call_and_get_x (it: &'_ ComplicatedStruct)
-> i32
{
(it.cb)(&it.path);
it.x
}
#[ffi_export]
fn destroy (it: repr_c::Box<ComplicatedStruct>)
{
drop(it)
}
Generated C header
/* Forward declaration */
typedef struct ComplicatedStruct ComplicatedStruct_t;
ComplicatedStruct_t * create (void);
int32_t call_and_get_x (
ComplicatedStruct_t const * it);
void destroy (
ComplicatedStruct_t * it);
Testing it from C
#include <assert.h>
#include <stdlib.h>
#include "dem_header.h"
int main (
int argc,
char const * const argv[])
{
ComplicatedStruct_t * it = create();
assert(call_and_get_x(it) == 42); // Prints 'path = `/tmp`'
destroy(it);
return EXIT_SUCCESS;
}
Going further
Transparent newtype wrapper
use ::safer_ffi::{prelude::*, ptr};
/// A `Box`-like owned pointer type, but which can be freed using `free()`.
#[derive_ReprC]
#[repr(transparent)]
pub struct Malloc<T>(ptr::NonNullOwned<T>);
impl<T> Malloc<T> {
pub fn new(value: T) -> Option<Self> {
/* Uses `posix_memalign()` to handle the allocation */
}
}
This pattern allows you to define a new type with thus specific Rust semantics attached to it (e.g., specific constructor, destructor and methods) while hiding all that to the C side:
- in the C world,
Malloc<T>
will be referred to in the same way thatptr::NonNullOwned<T>
is, i.e., as a (non-nullable)*mut T
.
Example
#[ffi_export]
fn new_int (x: i32)
-> Option<Malloc<i32>>
{
Malloc::new(x)
}
would then generate:
int32_t * new_int (
int32_t x);