diff --git a/trixy-macros/src/generate/c_api/header/mod.rs b/trixy-macros/src/generate/c_api/header/mod.rs
index 5c1786a..b047bed 100644
--- a/trixy-macros/src/generate/c_api/header/mod.rs
+++ b/trixy-macros/src/generate/c_api/header/mod.rs
@@ -36,21 +36,21 @@ mod pure_header;
mod structs_init;
mod typedef;
-const BEGIN_HEADER_GUARD: &'static str = r"#ifndef TRIXY_MAIN_HEADER
+const BEGIN_HEADER_GUARD: &'static str = r"#if !defined TRIXY_MAIN_HEADER
#define TRIXY_MAIN_HEADER";
-const END_HEADER_GUARD: &'static str = r"#endif // ifndef TRIXY_MAIN_HEADER";
+const END_HEADER_GUARD: &'static str = r"#endif // if !defined TRIXY_MAIN_HEADER";
-/// This function acts as the core transformative aspect, turning this trixy code into the
+/// This function acts as the core transformative aspect, turning this Trixy code into the
/// following c header:
///
/// *Trixy:*
/// ```text
/// fn fn_alone();
-/// nasp one {
+/// mod one {
/// fn fn_one();
-/// nasp two {
+/// mod two {
/// fn fn_two();
-/// nasp three {
+/// mod three {
/// fn fn_three();
/// }
/// }
@@ -58,7 +58,7 @@ const END_HEADER_GUARD: &'static str = r"#endif // ifndef TRIXY_MAIN_HEADER";
/// ```
/// *C header:*
/// ```text
-/// #ifndef TRIXY_MAIN_HEADER
+/// #if !defined TRIXY_MAIN_HEADER
/// #define TRIXY_MAIN_HEADER
///
/// extern int fn_alone();
@@ -66,30 +66,30 @@ const END_HEADER_GUARD: &'static str = r"#endif // ifndef TRIXY_MAIN_HEADER";
/// extern int one_two_fn_two();
/// extern int one_two_three_fn_three();
///
-/// typedef struct {
+/// struct three {
/// void (*fn_three)(void);
-/// } three_t;
-/// typedef struct {
+/// };
+/// struct two {
/// void (*fn_two)(void);
/// three_t three;
-/// } two_t;
-/// typedef struct {
+/// };
+/// struct one {
/// void (*fn_one)(void);
/// two_t two;
-/// } one_t;
+/// } ;
///
-/// const three_t three = {
+/// const struct three three = {
/// .fn_three = one_two_three_fn_three,
/// };
-/// const two_t two = {
+/// const struct two two = {
/// .fn_two = one_two_fn_two,
/// .three = three,
/// };
-/// const one_t one = {
+/// const struct one one = {
/// .fn_one = one_fn_one,
/// .two = two,
/// };
-/// #endif // ifndef TRIXY_MAIN_HEADER
+/// #endif // if !defined TRIXY_MAIN_HEADER
/// ```
pub fn generate(trixy: &CommandSpec, _config: &TrixyConfig) -> String {
let pure_header = pure_header::generate(&trixy);
diff --git a/trixy-macros/src/generate/c_api/header/pure_header.rs b/trixy-macros/src/generate/c_api/header/pure_header.rs
index 9219f4e..a768471 100644
--- a/trixy-macros/src/generate/c_api/header/pure_header.rs
+++ b/trixy-macros/src/generate/c_api/header/pure_header.rs
@@ -54,10 +54,10 @@ fn structure_to_header(structure: &Structure) -> String {
format!(
"
{}
- typedef struct {{
+ struct {} {{
{}
- }} {};",
- doc_comments, contents, ident
+ }};",
+ doc_comments, ident, contents
)
}
fn doc_named_type_to_header(doc_named_type: &DocNamedType) -> String {
@@ -94,10 +94,10 @@ fn enumeration_to_header(enumeration: &Enumeration) -> String {
format!(
"
{}
- typedef enum {{
+ enum {} {{
{}
- }} {};",
- doc_comments, states, ident
+ }};",
+ doc_comments, ident, states
)
}
fn doc_identifier_to_header(doc_identifier: &DocIdentifier) -> String {
diff --git a/trixy-macros/src/generate/c_api/header/typedef.rs b/trixy-macros/src/generate/c_api/header/typedef.rs
index 611c534..99e24d8 100644
--- a/trixy-macros/src/generate/c_api/header/typedef.rs
+++ b/trixy-macros/src/generate/c_api/header/typedef.rs
@@ -51,15 +51,15 @@ fn function_to_typedef(function: &Function) -> TokenStream2 {
fn namespace_to_typedef(namespace: &Namespace) -> TokenStream2 {
let ident = identifier_to_rust(&namespace.name);
- let type_ident = format_ident!("{}_t", ident.to_string());
+ let type_ident = format_ident!("{}", ident.to_string());
quote! {
- #type_ident #ident ;
+ struct #type_ident #ident;
}
}
fn namespace_to_full_typedef(nasp: &Namespace) -> String {
- let ident = format_ident!("{}_t", nasp.name.name);
+ let ident = format_ident!("{}", nasp.name.name);
let doc_comments = nasp
.attributes
.iter()
@@ -84,10 +84,10 @@ fn namespace_to_full_typedef(nasp: &Namespace) -> String {
.join("\n");
let namespace = quote! {
- typedef struct {
+ struct #ident {
#functions
#namespaces
- } #ident;
+ };
};
format! {"{}\n{}{}\n", next_namespace, doc_comments, namespace}
}
diff --git a/trixy-macros/src/generate/c_api/host.rs b/trixy-macros/src/generate/c_api/host.rs
index 8f9df40..65c5873 100644
--- a/trixy-macros/src/generate/c_api/host.rs
+++ b/trixy-macros/src/generate/c_api/host.rs
@@ -27,7 +27,7 @@ use crate::{
config::TrixyConfig,
generate::{
c_api::{mangle_c_function_ident, namespaces_to_path, type_to_c_equalivalent},
- function_identifier_to_rust, identifier_to_rust, named_type_to_rust,
+ function_identifier_to_rust, identifier_to_rust,
},
};
diff --git a/trixy-types/Cargo.toml b/trixy-types/Cargo.toml
index 379ee6d..9d2e2f5 100644
--- a/trixy-types/Cargo.toml
+++ b/trixy-types/Cargo.toml
@@ -30,4 +30,3 @@ proc-macro2 = "1.0.70"
quote = "1.0.33"
syn = { version = "2.0.41", features = ["extra-traits", "full", "parsing"] }
thiserror = "1.0.51"
-trixy-types-derive = { path = "./trixy-types-derive" }
diff --git a/trixy-types/src/error/mod.rs b/trixy-types/src/error/mod.rs
index 884b320..c6ae1a2 100644
--- a/trixy-types/src/error/mod.rs
+++ b/trixy-types/src/error/mod.rs
@@ -32,4 +32,10 @@ pub enum TypeConversionError {
#[error("You passed a untyped (type_id == Unknown) value to the conversion function!")]
UntypedInput,
+
+ #[error("You passed an unaligned pointer to a function expecting a pointer aligned to it's required value!")]
+ NotAligned,
+
+ #[error("The returned Result was an error: {original_message}")]
+ ResultWasErr { original_message: String },
}
diff --git a/trixy-types/src/lib.rs b/trixy-types/src/lib.rs
index 694eff4..41f0507 100644
--- a/trixy-types/src/lib.rs
+++ b/trixy-types/src/lib.rs
@@ -18,7 +18,7 @@
* If not, see .
*/
-//! Trixy contains the types used by the [trixy-macros] crate to provide ffi safe types
+//! Trixy contains the types used by the [`trixy-macros`] crate to provide ffi safe types
pub mod error;
pub mod traits;
pub mod types_list;
@@ -33,28 +33,32 @@ macro_rules! header {
/// These are the "primitive" types used in Trixy, you can use any of them to create new structures
/// The str is the name, the index represents the expected generic arguments
-pub const BASE_TYPES: [(&'static std::primitive::str, usize); 5] = [
- ("str", 0),
+///
+// NOTE: Every type here must have the [`Convertible`] implemented on it (@soispha)
+pub const BASE_TYPES: [(&'static std::primitive::str, usize); 14] = [
+ // Unsigned
+ ("u8", 0),
+ ("u16", 0),
+ ("u32", 0),
+ ("u64", 0),
+ // Signed
+ ("i8", 0),
+ ("i16", 0),
+ ("i32", 0),
+ ("i64", 0),
+ // Float
+ ("f32", 0),
+ ("f64", 0),
+ // Other
("String", 0),
- ("Result", 2),
("Option", 1),
("Vec", 1),
+ ("Result", 2),
];
/// The first value is the file name, the second it's contents
-pub const C_TYPE_HEADER: [(&'static std::primitive::str, &'static std::primitive::str); 9] = [
- header!("errno.h"),
- // These must be *before* "type_id.h"
- header!("type_id_dec.h"),
- header!("result_dec.h"),
- header!("option.h"),
- header!("string.h"),
- header!("vec_dec.h"),
-
- header!("type_id.h"),
- header!("result.h"),
- header!("vec.h"),
-];
+pub const C_TYPE_HEADER: [(&'static std::primitive::str, &'static std::primitive::str); 3] =
+ [header!("errno.h"), header!("string.h"), header!("vec.h")];
pub fn header_names() -> std::string::String {
C_TYPE_HEADER
diff --git a/trixy-types/src/traits/convert_trait.rs b/trixy-types/src/traits/convert_trait.rs
index 0934416..13b761e 100644
--- a/trixy-types/src/traits/convert_trait.rs
+++ b/trixy-types/src/traits/convert_trait.rs
@@ -1,108 +1,166 @@
+use crate::error::{self, TypeConversionError};
use std::{
- any::Any,
- ffi::{CStr, CString},
- os::raw::c_void,
+ error::Error,
+ ffi::{c_char, CString},
+ mem::ManuallyDrop,
+ ptr,
};
-/// Convert a value
+use super::errno;
+
+/// Convert a value into a data structure that we can send directly to c.
pub trait Convertible {
- /// Turn the value into a c void pointer.
- /// It should try its best to return a value which c can understand.
- /// If this however is not possible, it should still return the underlying raw data
- fn into_void(self) -> *const c_void;
+ type Ptr;
+ /// Turn the value into a c void pointer or a data structure that c can understand.
+ fn into_ptr(self) -> Self::Ptr;
+
+ /// If the data returned by [`into_ptr`] needs to be dropped this function should do it.
+ fn drop_ptr(ptr: Self::Ptr) -> Result<(), error::TypeConversionError>;
}
-impl Convertible for crate::String {
- fn into_void(self) -> *const c_void {
- self.0 as *const _ as *const c_void
+macro_rules! primitive_convert {
+ [$name:ty] => {
+ impl Convertible for $name {
+ type Ptr = $name;
+
+ fn into_ptr(self) -> Self::Ptr {
+ self
+ }
+
+ fn drop_ptr(_: Self::Ptr) -> Result<(), error::TypeConversionError> {
+ unreachable!("Theres is no need to drop a {}", stringify!($name))
+ }
+ }
}
}
-impl Convertible for crate::str {
- fn into_void(self) -> *const c_void {
- self.0 as *const _ as *const c_void
- }
-}
+// Unsigned
+primitive_convert!(u8);
+primitive_convert!(u16);
+primitive_convert!(u32);
+primitive_convert!(u64);
-impl Convertible for crate::Option {
- fn into_void(mut self) -> *const c_void {
- &mut self as *const _ as *const c_void
- }
-}
+// Signed
+primitive_convert!(i8);
+primitive_convert!(i16);
+primitive_convert!(i32);
+primitive_convert!(i64);
-impl Convertible for crate::Vec {
- fn into_void(mut self) -> *const c_void {
- &mut self as *const _ as *const c_void
- }
-}
-
-impl Convertible for crate::Result {
- fn into_void(mut self) -> *const c_void {
- &mut self as *const _ as *const c_void
- }
-}
-
-impl Convertible
- for Result
-{
- fn into_void(self) -> *const c_void {
- Into::::into(self).into_void()
- }
-}
-
-impl Convertible for &CStr {
- fn into_void(self) -> *const c_void {
- Into::::into(self).into_void()
- }
-}
+// Float
+primitive_convert!(f32);
+primitive_convert!(f64);
impl Convertible for CString {
- fn into_void(self) -> *const c_void {
- Into::::into(self).into_void()
+ type Ptr = *const *const c_char;
+
+ fn into_ptr(self) -> Self::Ptr {
+ self.into_raw() as *const *const c_char
+ }
+
+ fn drop_ptr(ptr: Self::Ptr) -> Result<(), error::TypeConversionError> {
+ unsafe {
+ if ptr.is_null() {
+ return Err(TypeConversionError::NullPointer);
+ }
+ // TODO(@soispha): Add this, when this feature is stable <2024-02-17>
+ // if !ptr.is_aligned() {
+ // return Err(TypeConversionError::NotAligned);
+ // }
+
+ // SAFETY:
+ // Checked that the ptr is not null and that it is aligned (to c_char).
+ // This should hopefully be enough to avoid constructing a CString from a pointer not
+ // constructed by a us earlier in the [`into_ptr`] function.
+ // However:
+ // This still builds on the hope that c passes the pointer with the length unchanged.
+ let string = CString::from_raw(ptr as *mut i8);
+ drop(string);
+ }
+ Ok(())
}
}
-impl Convertible for () {
- fn into_void(self) -> *const c_void {
- std::ptr::null()
+impl Convertible for Option {
+ type Ptr = *const T;
+
+ fn into_ptr(self) -> Self::Ptr {
+ if let Some(inner) = self {
+ let mut_ref = &mut inner.into_ptr();
+ mut_ref as *mut _ as *const T
+ } else {
+ ptr::null()
+ }
+ }
+
+ fn drop_ptr(_: Self::Ptr) -> Result<(), error::TypeConversionError> {
+ unreachable!(
+ "
+ This should never be called, as this option type resolves
+ to a null pointer or to a pointer to the real value
+ (e. g. a `crate::Vec`)
+ "
+ );
}
}
-extern crate trixy_types_derive;
-pub use trixy_types_derive::{Convertible, TypeInfo};
+impl Convertible for Result {
+ type Ptr = *const T;
-/// Similar to [`std::any::type_name`] but only really works for *some* types.
-/// If the type is unknown it will just return [`TypeId::Unknown`]
-pub trait TypeInfo {
- fn type_of(&self) -> crate::TypeId;
+ fn into_ptr(self) -> Self::Ptr {
+ match self {
+ Ok(ok) => {
+ let mut_ref = &mut ok.into_ptr();
+ mut_ref as *mut _ as *const T
+ }
+ Err(err) => {
+ errno::set(TypeConversionError::ResultWasErr {
+ original_message: err.to_string(),
+ });
+ ptr::null()
+ }
+ }
+ }
+
+ fn drop_ptr(_: Self::Ptr) -> Result<(), error::TypeConversionError> {
+ unreachable!(
+ "
+ This should never be called, as this result type resolves
+ to a null pointer (and a set error) or to a pointer to
+ the real value (e. g. a `crate::Vec`)
+ "
+ );
+ }
}
-macro_rules! type_info {
- ($name:tt<_>, $output:ident) => {
- impl TypeInfo for $name {
- fn type_of(&self) -> crate::TypeId {
- crate::TypeId::$output
- }
+impl Convertible for Vec {
+ type Ptr = crate::Vec;
+
+ fn into_ptr(self) -> Self::Ptr {
+ let mut me = ManuallyDrop::new(self);
+ Self::Ptr {
+ data: me.as_mut_ptr() as *const T,
+ length: me.len(),
+ capacity: me.capacity(),
}
- };
- ($name:tt<_,_>, $output:ident) => {
- impl TypeInfo for $name {
- fn type_of(&self) -> crate::TypeId {
- crate::TypeId::$output
+ }
+
+ fn drop_ptr(ptr: Self::Ptr) -> Result<(), error::TypeConversionError> {
+ let vec = unsafe {
+ if ptr.data.is_null() {
+ return Err(TypeConversionError::NullPointer);
}
- }
- };
- ($name:ty, $output:ident) => {
- impl TypeInfo for $name {
- fn type_of(&self) -> crate::TypeId {
- crate::TypeId::$output
- }
- }
- };
+
+ // TODO(@soispha): Add this, when this feature is stable <2024-02-17>
+ // if !ptr.data.is_aligned() {
+ // return Err(TypeConversionError::NotAligned);
+ // }
+
+ // SAFETY:
+ // We simply hope that c treated our vector as read-only and didn't modify it.
+ // See the SAFETY section of the String implementation for more detail.
+ Vec::from_raw_parts(ptr.data as *mut T, ptr.length, ptr.capacity)
+ };
+ drop(vec);
+ Ok(())
+ }
}
-type_info!((), void);
-type_info!(&CStr, str_t);
-type_info!(CString, string_t);
-type_info!(Result<_, _>, result_t);
-type_info!(Vec<_>, vec_t);
-type_info!(Option<_>, option_t);
diff --git a/trixy-types/src/traits/mod.rs b/trixy-types/src/traits/mod.rs
index ac32f2d..62ad4b8 100644
--- a/trixy-types/src/traits/mod.rs
+++ b/trixy-types/src/traits/mod.rs
@@ -18,212 +18,5 @@
* If not, see .
*/
-use std::{
- alloc::Layout,
- ffi::{c_char, CStr, CString},
- mem::ManuallyDrop,
- ptr,
-};
-
-use crate::error::TypeConversionError;
-
-use self::convert_trait::{Convertible, TypeInfo};
-
pub mod convert_trait;
pub mod errno;
-
-impl<'a> TryFrom<&'a crate::str> for &'a str {
- type Error = crate::error::TypeConversionError;
-
- fn try_from(value: &'a crate::str) -> Result {
- let ptr = value.0;
- if ptr.is_null() {
- Err(TypeConversionError::NullPointer)
- } else {
- // SAFETY: We checked for null, which is a huge upside other things that we simply hope
- // for:
- // - null terminated
- // - actually be a valid pointer (`(void*) 2` is *valid* c but not a *valid* pointer)
- // - be contained in a single allocated object
- // - the memory must obviously not be mutated, while this &str exists
- // - the null terminator must be within `isize::MAX` from the start position
- let str = unsafe { CStr::from_ptr(*ptr as *const c_char) };
- str.to_str()
- .map_err(|_err| crate::error::TypeConversionError::String {
- got: value.0 as *const c_char,
- })
- }
- }
-}
-impl TryFrom for String {
- type Error = crate::error::TypeConversionError;
-
- fn try_from(value: crate::String) -> Result {
- let ptr = value.0;
- if ptr.is_null() {
- Err(TypeConversionError::NullPointer)
- } else {
- // SAFETY: (exactly the same as above applies)
- let string = unsafe { CStr::from_ptr(ptr) }.to_owned();
- Ok(string
- .into_string()
- .map_err(|err| TypeConversionError::String {
- got: err.into_cstring().into_raw(),
- })?)
- }
- }
-}
-
-impl TryFrom for Vec {
- type Error = crate::error::TypeConversionError;
-
- fn try_from(value: crate::Vec) -> Result {
- match value.type_id {
- crate::TypeId::Unknown => Err(Self::Error::UntypedInput),
- crate::TypeId::void => Ok(vec![]),
- crate::TypeId::str_t => {
- let mut output: Vec = Vec::with_capacity(value.capacity);
- for i in 0..value.size {
- // output.push(value.data.add())
- }
- Ok(output)
- },
- crate::TypeId::string_t => todo!(),
- crate::TypeId::result_t => todo!(),
- crate::TypeId::vec_t => todo!(),
- crate::TypeId::option_t => todo!(),
- }
- }
-}
-
-impl crate::Vec {
- pub fn pop(&mut self) -> Result {
- match self.type_id {
- crate::TypeId::Unknown => Err(TypeConversionError::UntypedInput),
- crate::TypeId::void => Ok(() as T),
- crate::TypeId::str_t => todo!(),
- crate::TypeId::string_t => todo!(),
- crate::TypeId::result_t => todo!(),
- crate::TypeId::vec_t => todo!(),
- crate::TypeId::option_t => todo!(),
- }
- }
-}
-
-impl From for crate::String {
- fn from(value: CString) -> Self {
- let layout = Layout::array::(value.as_bytes_with_nul().len()).expect(
- "We don't handle n > isize::MAX, in the other types. \
- Thus, this convertion will fail",
- );
- // SAFETY:
- // - The layout should never have zero size (at least 1)
- // - The memory blocks are copied from the string, thus initialization is irrelevant
- let ptr = unsafe { std::alloc::alloc(layout) };
- if ptr.is_null() {
- // TODO(@soispha): How do we handle this? <2023-12-27>
- panic!("While preparing a string for c failed to allocate memory, the pointer is null");
- }
-
- // SAFETY:
- // - Both are valid for reads of `count * size_of::()` bytes because we checked
- // their length
- // - They are properly aligned for types of [`c_char`]
- unsafe {
- ptr::copy(
- value.as_ptr(),
- ptr as *mut i8,
- value.as_bytes_with_nul().len(),
- )
- };
-
- Self(ptr as *const c_char)
- }
-}
-
-impl<'a> From<&'a CStr> for crate::str {
- fn from(value: &'a CStr) -> Self {
- crate::str(value.as_ptr() as *const u8)
- }
-}
-
-impl From> for crate::Vec
-where
- T: Convertible + TypeInfo,
-{
- fn from(value: Vec) -> Self {
- if value.is_empty() {
- Self {
- type_id: crate::TypeId::Unknown,
- data: ptr::null(),
- size: 0,
- capacity: 0,
- }
- } else {
- let value_type = value.first().expect("We checked the length").type_of();
- let value: Vec<_> = value.into_iter().map(|val| val.into_void()).collect();
-
- // We simply tell rust, that c should drop the value
- let vec = ManuallyDrop::new(value);
- Self {
- type_id: value_type,
- data: *vec.first().expect("This exists"),
- size: vec.len(),
- capacity: vec.capacity(),
- }
- }
- }
-}
-
-impl From