Cglue
Rust ABI safe code generator
Install / Use
/learn @h33p/CglueREADME
CGlue
If all code is glued together, our glue is the safest on the market.
The most complete dynamic trait object implementation, period.
<!-- toc --> <!-- /toc -->Overview
CGlue exposes dyn Trait in FFI-safe manner. It bridges Rust traits between C and other
languages. It aims to be seamless to integrate - just add a few annotations around your traits,
and they should be good to go!
use cglue::*;
// One annotation for the trait.
#[cglue_trait]
pub trait InfoPrinter {
type Mark;
fn print_info(&self, mark: Self::Mark);
}
struct Info {
value: usize
}
impl InfoPrinter for Info {
type Mark = u8;
fn print_info(&self, mark: Self::Mark) {
println!("{} - info struct: {}", mark, self.value);
}
}
fn use_info_printer<T: InfoPrinter>(printer: &T, mark: T::Mark) {
println!("Printing info:");
printer.print_info(mark);
}
fn main() -> () {
let mut info = Info {
value: 5
};
// Here, the object is fully opaque, and is FFI and ABI safe.
let obj = trait_obj!(&mut info as InfoPrinter);
use_info_printer(&obj, 42);
}
Rust does not guarantee your code will work with neither 2 different compiler versions clashing, nor any other minor changes, CGlue glues it all together in a way that works.
This is done by generating wrapper vtables (virtual function tables) for the specified trait, and creating an opaque object with matching table.
cglue_trait annotation generates a InfoPrinterVtbl structure, and all the code needed to construct it for a type implementing the InfoPrinter trait. Then, a CGlueTraitObj is constructed that wraps the input object and implements the InfoPrinter trait.
But that's not all, you can also group traits together!
use cglue::*;
// Extra trait definitions
#[cglue_trait]
pub trait InfoChanger {
fn change_info(&mut self, new_val: usize);
}
impl InfoChanger for Info {
fn change_info(&mut self, new_val: usize) {
self.value = new_val;
}
}
#[cglue_trait]
pub trait InfoDeleter {
fn delete_info(&mut self);
}
// Define a trait group.
//
// Here, `InfoPrinter` is mandatory - always required to be implemented,
// whereas `InfoChanger` with `InfoDeleter` are optional traits - a checked
// cast must be performed to access them.
cglue_trait_group!(InfoGroup, InfoPrinter, { InfoChanger, InfoDeleter });
// Implement the group for `Info` structure, defining
// only that `InfoChanger` is optionally implemented.
// This is not required if `unstable` feature is being used!
cglue_impl_group!(Info, InfoGroup, InfoChanger);
let mut info = Info { value: 5 };
let mut obj = group_obj!(info as InfoGroup);
// Object does not implement `InfoDeleter`
assert!(as_ref!(&obj impl InfoDeleter).is_none());
change_info(&mut cast!(obj impl InfoChanger).unwrap(), 20);
fn change_info(change: &mut (impl InfoPrinter + InfoChanger), new_val: usize) {
println!("Old info:");
change.print_info();
change.change_info(new_val);
println!("New info:");
change.print_info();
}
And there is much more! Here are some highlights:
-
Ability to use self-consuming trait functions.
-
Some standard library traits are exposed (
Clone). -
Ability to wrap associated trait types into new CGlue trait objects and groups.
-
The above ability also works with mutable, and const reference associated type returns*.
-
Generic traits and their groups.
-
Optional runtime ABI/API validation with abi_stable (enable
layout_checksfeature).
In-depth look
Safety assumptions
This crate relies on the assumption that opaque objects will not be tampered with, that is vtable functions will not be modified. It is being ensured through encapsulation of fields from anywhere by using hidden submodules. However, unverifiable users (C libraries) may still be able to modify the tables. This library assumes they are not malicious and does not perform any runtime verification. API version mismatch checking with abi_stable is an opt-in feature (requires rustc 1.46+).
Other than 2 bits in associated type wrapping, this crate should be safe.
The crate employs a number of unsafe traits that get auto-implemented, or traits with unsafe
functions. Their usage inside the code generator should be safe, they are marked in such a way
so that manual implementations can not introduce undefined behaviour.
Name generation
#[cglue_trait] macro for MyTrait will generate the following important types:
| Name | Purpose | Instance type | Context |
--- | --- | --- | ----
| MyTraitBox | Regular owned CGlue object. | CBox<c_void> | NoContext |
| MyTraitCtxBox<Ctx> | Owned CGlue object with a context. | CBox<c_void> | Ctx |
| MyTraitArcBox | Owned CGlue object with a reference counted context. | CBox<c_void> | CArc<c_void> |
| MyTraitMut | By-mut-ref CGlue object. | &mut c_void. | NoContext |
| MyTraitCtxMut<Ctx> | By-mut-ref CGlue object with a context. | &mut c_void. | Ctx |
| MyTraitArcMut | By-mut-ref CGlue object with a reference counted context. | &mut c_void. | CArc<c_void> |
| MyTraitRef | By-ref (const) CGlue object. | &c_void. | NoContext |
| MyTraitCtxRef<Ctx> | By-ref (const) CGlue object with a context. | &c_void. | Ctx |
| MyTraitArcRef | By-ref (const) CGlue object with a reference counted context. | &c_void. | CArc<c_void> |
Only opaque types provide functionality. Non-opaque types can be used as Into trait bounds
and are required to type check trait bounds.
These are the generic types needed for bounds checking:
| Name | Purpose | Instance type | Context |
--- | --- | --- | ---
| MyTraitBaseBox<T> | Base owned CGlue object. | CBox<T> | NoContext |
| MyTraitBaseCtxBox<T, Ctx> | Base owned CGlue object with some context. | CBox<T> | Ctx |
| MyTraitBaseArcBox<T, Ctx> | Base owned CGlue object with reference counted context. | CBox<T> | CArc<Ctx> |
| MyTraitBaseMut<T> | Base by-mut-ref CGlue object. | &mut T. | NoContext |
| MyTraitBaseRef<T> | Typedef for generic by-ref (const) CGlue object. | &T. | NoContext |
| MyTraitBase<Inst, Ctx> | Base (non-opaque) CGlue object. It can have any compatible instance and context | Inst | Ctx |
Finally, the following underlying types exist, but do not need to be interacted with in Rust:
| Name | Purpose |
--- | ---
| MyTraitVtbl<C> | Table of all functions of the trait. Should be opaque to the user. |
| MyTraitRetTmp<Ctx> | Structure for temporary return values. It should be opaque to the user. |
Instead, every opaque CGlue object implements MyTraitOpaqueObj trait, which contains the type
of the vtable.
cglue_trait_group! macro for MyGroup will generate the following main types:
| Name | Purpose | Instance type | Context |
--- | --- | --- | ---
| MyGroupBox | Owned CGlue trait group. | CBox<c_void> | NoContext |
| MyGroupCtxBox<Ctx> | Owned CGlue trait group with some context. | CBox<c_void> | Ctx |
| MyGroupArcBox | Typedef for opaque owned CGlue trait group with reference counted context. | CBox<c_void> | CArc<c_void> |
| MyGroupMut | Typedef for opaque by-mut-ref CGlue trait group. | &mut c_void. | NoContext |
| MyGroupCtxMut<Ctx> | Typedef for opaque by-mut-ref CGlue trait group with a custom context. | &mut c_void. | Ctx |
| MyGroupArcMut | Typedef for opaque by-mut-ref CGlue trait group with a reference counted context. | &mut c_void. | CArc<c_void> |
| MyGroupRef | Typedef for opaque by-ref (const) CGlue trait group. | &c_void. | [NoContext](crate::trait_grou
