feat(src): Add real and working support for results

Every `Result` is now -- on demand -- instantiated (i.e. the generic types
are replaced with the concretely specified ones). And can thus simply be
exported to c.
This commit is contained in:
Benedikt Peetz 2024-05-20 15:19:11 +02:00
parent ee8e860789
commit 9000c1de02
Signed by: bpeetz
GPG Key ID: B6139BCB07CE946D
15 changed files with 436 additions and 170 deletions

View File

@ -25,7 +25,7 @@ use crate::parser::command_spec::{Attribute, DocIdentifier, Enumeration};
impl Enumeration {
pub fn to_auxiliary_c(&self) -> String {
let doc_comments: String = Attribute::to_auxiliary_c_merged(&self.attributes);
let ident = &self.identifier.to_auxiliary_c();
let ident = &self.identifier.to_auxiliary_c(&[]);
let states = self
.states
.iter()

View File

@ -24,15 +24,12 @@ use convert_case::{Case, Casing};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use crate::parser::command_spec::{Identifier, Variant};
use crate::parser::command_spec::{Identifier, Type, Variant};
mod doc_identifier;
//
// pub use doc_identifier::*;
impl Identifier {
pub fn to_auxiliary_c(&self) -> TokenStream2 {
pub fn to_auxiliary_c(&self, generic_args: &[Type]) -> TokenStream2 {
let ident = self.to_rust_pascalized();
match &self.variant {
Variant::Structure { .. } => {
@ -46,10 +43,17 @@ impl Identifier {
enum #ident
}
}
Variant::Result { .. } => {
let ident = Type::mangle_result_name(generic_args);
quote! {
struct #ident
}
}
Variant::Primitive => match self.name.to_case(Case::Snake).as_str() {
"string" => {
quote! {
const char*
const char *
}
}
// Unsigned
@ -65,14 +69,15 @@ impl Identifier {
// Float
"f_32" => quote! { float },
"f_64" => quote! { double },
// Other (not yet imlemented)
// Others (not yet imlemented)
// ("Option", 1),
// ("Vec", 1),
// ("Result", 2),
other => {
todo!("'{}' is not yet supported", other)
}
},
other => {
unimplemented!("{:#?}", other)
}

View File

@ -24,7 +24,7 @@ use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use crate::parser::command_spec::{
Attribute, Enumeration, Function, Identifier, Namespace, Structure,
Attribute, Enumeration, Function, Identifier, Namespace, Structure, Type,
};
impl Namespace {
@ -44,6 +44,12 @@ impl Namespace {
.map(Enumeration::to_auxiliary_c)
.collect::<Vec<String>>()
.join("\n");
let results: String = self
.select_types_pred(|(ident, _generics)| ident.name.as_str() == "Result")
.iter()
.map(|(_, generics)| Type::to_auxilary_c_result(generics).to_string())
.collect::<Vec<String>>()
.join("\n");
let functions: String = self
.functions
.iter()
@ -56,7 +62,7 @@ impl Namespace {
.map(|nasp| nasp.to_auxiliary_c(&nasps))
.collect();
format! {"{}\n{}\n{}\n{}", enumerations, structures, functions, namespaces}
format! {"{}\n{}\n{}\n{}\n{}", enumerations, structures, results, functions, namespaces}
}
pub fn to_auxiliary_c_full_struct_init(&self, namespaces: &Vec<&Identifier>) -> TokenStream2 {

View File

@ -25,7 +25,7 @@ use crate::parser::command_spec::{Attribute, DocNamedType, Structure};
impl Structure {
pub fn to_auxiliary_c(&self) -> String {
let doc_comments: String = Attribute::to_auxiliary_c_merged(&self.attributes);
let ident = self.identifier.to_auxiliary_c();
let ident = self.identifier.to_auxiliary_c(&[]);
let contents = self
.contents
.iter()

View File

@ -36,9 +36,9 @@ impl Type {
match self {
Type::Typical {
identifier,
generic_args: _,
generic_args,
} => {
let ident = identifier.to_auxiliary_c();
let ident = identifier.to_auxiliary_c(generic_args);
let output = if is_output {
quote! {
*
@ -78,4 +78,21 @@ impl Type {
}
}
}
pub fn to_auxilary_c_result(generic_args: &[Type]) -> TokenStream2 {
let ident = Type::mangle_result_name(&generic_args);
let generic_one = generic_args[0].to_auxiliary_c(false, None);
let generic_two = generic_args[1].to_auxiliary_c(false, None);
quote! {
struct #ident {
enum ResultTag tag;
union {
#generic_one ok;
#generic_two err;
} value;
};
}
}
}

View File

@ -20,12 +20,14 @@
* If not, see <https://www.gnu.org/licenses/>.
*/
use std::iter;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use crate::{
macros::config::trixy::TrixyConfig,
parser::command_spec::{Enumeration, Identifier, Namespace, Structure},
parser::command_spec::{Enumeration, Identifier, Namespace, Structure, Type},
};
impl Namespace {
@ -48,11 +50,20 @@ impl Namespace {
let enumerations: TokenStream2 = self.enumerations.iter().map(Enumeration::to_c).collect();
let callback_function = format_ident!("{}", config.callback_function);
let all_results_types: TokenStream2 = self
.select_types_pred(|(identifier, _generic_args)| identifier.name.as_str() == "Result")
.iter()
.map(|(_, generics)| Type::to_c_result(&generics))
.collect();
quote! {
pub mod #ident {
#[allow(unused_imports)]
use crate :: #callback_function;
#all_results_types
#enumerations
#structures
#additional_functions
@ -60,4 +71,81 @@ impl Namespace {
#functions
}
}
/// Get all the types used in this namespaces that conform to the predicate.
/// The predicate get's the identifier of a type and the generic arguments passed.
pub fn select_types_pred(
&self,
pred: fn(&(Identifier, Vec<Type>)) -> bool,
) -> Vec<(Identifier, Vec<Type>)> {
fn filter_type(ty: &Type) -> Vec<(Identifier, Vec<Type>)> {
match ty {
Type::Typical {
identifier,
generic_args,
} => {
let mut output = vec![];
output.extend(generic_args.iter().map(|ga| filter_type(ga)).flatten());
output.push((identifier.to_owned(), generic_args.to_owned()));
// Stop recursion and return
output
}
Type::Function { inputs, output } => {
let mut full_output = vec![];
full_output.extend(
inputs
.iter()
.map(|na| &na.r#type)
.map(|ty| filter_type(&ty))
.flatten(),
);
if let Some(out) = output {
full_output.extend(filter_type(out))
}
full_output
}
}
}
let all_types = self.collect_types();
let all_result_types: Vec<_> = all_types
.iter()
.map(|r#type| filter_type(r#type))
.flatten()
.filter(pred)
.collect();
all_result_types
}
/// Collect a vector of all types used in this namespace
pub fn collect_types(&self) -> Vec<Type> {
let structures_types = self
.structures
.iter()
.map(|st| st.contents.iter().map(|dc| dc.r#type.clone()))
.flatten();
self.functions
.iter()
.map(|r#fn| {
r#fn.inputs
.iter()
.map(|nt| Some(nt.r#type.clone()))
.chain({
if let Some(output) = &r#fn.output {
iter::once(Some(output.clone()))
} else {
iter::once(None)
}
})
.filter_map(|x| x)
})
.flatten()
.chain(structures_types)
.collect()
}
}

View File

@ -21,7 +21,8 @@
*/
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use quote::{format_ident, quote};
use syn::Ident;
use crate::parser::command_spec::{Identifier, NamedType, Type};
@ -39,10 +40,84 @@ impl Type {
}
}
pub fn mangle_result_name(generic_args: &[Type]) -> Ident {
assert_eq!(generic_args.len(), 2);
format_ident!(
"result_{}_{}",
generic_args[0].to_string(),
generic_args[1].to_string()
)
}
pub fn to_c_function(inputs: &[NamedType], output: &Option<Box<Type>>) -> TokenStream2 {
Type::to_rust_function(&inputs, &output)
}
pub fn to_c_result(generic_args: &[Type]) -> TokenStream2 {
assert_eq!(generic_args.len(), 2);
let first_generic = generic_args[0].to_c();
let second_generic = generic_args[1].to_c();
let ident = Type::mangle_result_name(&generic_args);
let value_ident = format_ident!("{}_value", ident);
quote! {
#[allow(non_camel_case_types)]
#[repr(C)]
pub union #value_ident {
ok: std::mem::ManuallyDrop<#first_generic>,
err: std::mem::ManuallyDrop<#second_generic>,
}
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct #ident {
tag: trixy::types::ResultTag,
value: #value_ident,
}
impl std::fmt::Debug for #ident {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.tag {
trixy::types::ResultTag::Ok => write!(f, "{:?}", unsafe { &self.value.ok }),
trixy::types::ResultTag::Err => write!(f, "{:?}", unsafe { &self.value.err }),
}
}
}
impl trixy::types::traits::convert_trait::Convertible
for crate::OurResult<#first_generic, #second_generic>
{
type Ptr = #ident;
fn into_ptr(self) -> Self::Ptr {
if self.value.is_ok() {
Self::Ptr {
tag: trixy::types::ResultTag::Ok,
value: #value_ident {
ok: std::mem::ManuallyDrop::new(self.value.expect("Is ok")),
},
}
} else {
Self::Ptr {
tag: trixy::types::ResultTag::Err,
value: #value_ident {
err: std::mem::ManuallyDrop::new(self.value.expect_err("Is err")),
},
}
}
}
fn from_ptr(_ptr: Self::Ptr) -> Result<Self, trixy::types::error::TypeConversionError> {
unreachable!("This should probably not be called?");
}
}
}
}
fn to_c_typical(identifier: &Identifier, generic_args: &Vec<Type>) -> TokenStream2 {
let trixy_build_in_types: Vec<&str> = crate::types::BASE_TYPES
.iter()
@ -75,7 +150,7 @@ impl Type {
#nasp_path #ident
}
} else {
debug_assert_eq!(trixy_build_in_types.len(), 1);
assert_eq!(trixy_build_in_types.len(), 1);
let type_name = trixy_build_in_types
.first()
@ -83,11 +158,28 @@ impl Type {
match *type_name {
"Result" => {
let ident_ok = &generic_args.first().expect("This is a result").to_c();
let ident_err = &generic_args.last().expect("This is a result").to_c();
assert_eq!(generic_args.len(), 2);
let namespaces_path = &identifier.variant.to_c_path();
let nasp_path = if namespaces_path.is_empty() {
quote! {
crate ::
}
} else {
let path = namespaces_path;
quote! {
#path ::
}
};
let ident = format_ident!(
"result_{}_{}",
generic_args[0].to_string(),
generic_args[1].to_string()
);
quote! {
// eg: <Result<TrainedDog, TrainingMistake> as Convertible>::Ptr,
<Result<#ident_ok, #ident_err> as Convertible>::Ptr
#nasp_path #ident
}
}
"Option" => {

View File

@ -45,13 +45,12 @@ impl Variant {
let main_namespace;
match self {
Variant::Structure { namespace } => {
Variant::Structure { namespace }
| Variant::Enumeration { namespace }
| Variant::Result { namespace } => {
main_namespace = mangle_namespace_name(namespace);
}
Variant::Enumeration { namespace } => {
main_namespace = mangle_namespace_name(namespace);
}
_ => unreachable!("This should never be called"),
other => unreachable!("This should never be called on '{:#?}'", other),
}
Namespace::to_rust_path_owned(&main_namespace[..])
}

View File

@ -36,10 +36,23 @@ pub fn generate(trixy: &CommandSpec, config: &TrixyConfig) -> String {
format!(
"\
/* C API */\n\
// Workaround rust rules when implementing a foreign trait on a foreign type
#[derive(Debug)]
pub struct OurResult<T, E> {{
value: Result<T, E>,
}}
impl<T, E> From<Result<T, E>> for OurResult<T, E> {{
fn from(value: Result<T, E>) -> Self {{
Self {{ value }}
}}
}}
/* The real C API */\n\
{}",
rust_code
)
}
/// This function generates the main c API provided by Trixy.
/// This works for example like this:
/// Turning this:

View File

@ -36,9 +36,14 @@ pub enum Variant {
Enumeration {
namespace: Vec<Identifier>,
},
Result {
namespace: Vec<Identifier>,
},
Namespace,
/// The first (implicit) namespace, containing everything
RootNamespace,
Function,
Primitive,
NamedType,
@ -59,7 +64,7 @@ impl Variant {
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Namespace {
pub name: Identifier,
@ -89,21 +94,21 @@ impl From<Namespace> for CommandSpec {
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Structure {
pub identifier: Identifier,
pub contents: Vec<DocNamedType>,
pub attributes: Vec<Attribute>,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Enumeration {
pub identifier: Identifier,
pub states: Vec<DocIdentifier>,
pub attributes: Vec<Attribute>,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Function {
pub identifier: Identifier,
pub inputs: Vec<NamedType>,
@ -206,7 +211,7 @@ pub struct NamedType {
pub r#type: Type,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct DocNamedType {
pub name: Identifier,
pub r#type: Type,

View File

@ -159,7 +159,7 @@ impl Parser {
let mut functions = vec![];
for function in namespace.functions {
functions.push(self.process_function(function)?);
functions.push(self.process_function(function, &previous_namespaces)?);
}
let mut namespaces = vec![];
for namespace in namespace.namespaces {
@ -187,14 +187,15 @@ impl Parser {
fn process_function(
&mut self,
mut function: UncheckedFunction,
parent_namespaces: &Vec<Identifier>,
) -> Result<Function, ParsingError> {
let identifier = mem::take(&mut function.identifier.kind).to_identifier(Variant::Function);
let mut inputs = vec![];
for input in function.inputs {
inputs.push(self.process_named_type(input)?);
inputs.push(self.process_named_type(input, parent_namespaces)?);
}
let output = if let Some(r#type) = function.output {
Some(self.process_type(r#type)?)
Some(self.process_type(r#type, parent_namespaces)?)
} else {
None
};
@ -269,7 +270,7 @@ impl Parser {
});
let mut contents = vec![];
for named_type in structure.contents {
contents.push(self.process_doc_named_type(named_type)?);
contents.push(self.process_doc_named_type(named_type, parent_namespaces)?);
}
Ok(Structure {
@ -282,19 +283,21 @@ impl Parser {
fn process_named_type(
&mut self,
mut named_type: UncheckedNamedType,
parent_namespaces: &Vec<Identifier>,
) -> Result<NamedType, ParsingError> {
let name: Identifier =
mem::take(&mut named_type.name.kind).to_identifier(Variant::NamedType);
let r#type: Type = self.process_type(named_type.r#type)?;
let r#type: Type = self.process_type(named_type.r#type, parent_namespaces)?;
Ok(NamedType { name, r#type })
}
fn process_doc_named_type(
&mut self,
mut doc_named_type: UncheckedDocNamedType,
parent_namespaces: &Vec<Identifier>,
) -> Result<DocNamedType, ParsingError> {
let name: Identifier =
mem::take(&mut doc_named_type.name.kind).to_identifier(Variant::DocNamedType);
let r#type: Type = self.process_type(doc_named_type.r#type)?;
let r#type: Type = self.process_type(doc_named_type.r#type, parent_namespaces)?;
Ok(DocNamedType {
name,
r#type,
@ -302,14 +305,18 @@ impl Parser {
})
}
fn process_type(&mut self, r#type: UncheckedType) -> Result<Type, ParsingError> {
fn process_type(
&mut self,
r#type: UncheckedType,
parent_namespaces: &Vec<Identifier>,
) -> Result<Type, ParsingError> {
match r#type {
UncheckedType::Typical {
identifier,
generic_args,
} => self.process_typical_type(identifier, generic_args),
} => self.process_typical_type(identifier, generic_args, parent_namespaces),
UncheckedType::Function { inputs, output } => {
self.process_function_type(inputs, output)
self.process_function_type(inputs, output, parent_namespaces)
}
}
}
@ -318,6 +325,7 @@ impl Parser {
&mut self,
mut type_identifier: Token,
generic_args: Vec<UncheckedType>,
parent_namespaces: &Vec<Identifier>,
) -> Result<Type, ParsingError> {
fn match_to_vector_struct(matches: Vec<&UncheckedStructure>) -> Vec<Identifier> {
matches
@ -401,7 +409,13 @@ impl Parser {
.iter()
.any(|(ident, _)| ident == &identifier.name)
{
variant = Variant::Primitive;
if &identifier.name == "Result" {
variant = Variant::Result {
namespace: parent_namespaces.clone(),
};
} else {
variant = Variant::Primitive;
}
} else {
return Err(ParsingError::TypeNotDeclared {
r#type: identifier,
@ -461,7 +475,7 @@ impl Parser {
let mut new_generic_args: Vec<checked::Type> = vec![];
for generic_arg in generic_args {
new_generic_args.push(self.process_type(generic_arg)?);
new_generic_args.push(self.process_type(generic_arg, parent_namespaces)?);
}
Ok(Type::Typical {
@ -473,15 +487,16 @@ impl Parser {
&mut self,
inputs: Vec<UncheckedNamedType>,
output: Option<Box<UncheckedType>>,
parent_namespaces: &Vec<Identifier>,
) -> Result<Type, ParsingError> {
let inputs = inputs
.into_iter()
.map(|input| self.process_named_type(input))
.map(|input| self.process_named_type(input, parent_namespaces))
.collect::<Result<Vec<NamedType>, ParsingError>>()?;
let mut new_output = None;
if let Some(output) = output {
new_output = Some(Box::new(self.process_type(*output)?));
new_output = Some(Box::new(self.process_type(*output, parent_namespaces)?));
}
Ok(Type::Function {

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the Lesser GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef TRIXY_RESULT_H
#define TRIXY_RESULT_H
/**
* @brief A rust-like result type
*
* @detail
* This macro effectively generates a type name from the two generic
* parameters. The generated type name is always globally unique.
*/
#define Result(ok, err) struct result_##ok##_##err
/**
* @brief The possibly states of a Result.
*/
enum ResultTag
{
OK,
ERR,
};
#endif // TRIXY_RESULT_H

View File

@ -40,7 +40,7 @@ macro_rules! header {
// NOTE: Every type here must have the [`Convertible`] implemented on it (@soispha)
// And be added to the `convert/c/auxiliary/idendentifier/mod.rs` file
// And add it to the types list (`./types_list.rs`)
pub const BASE_TYPES: [(&'static std::primitive::str, usize); 11] = [
pub const BASE_TYPES: [(&'static std::primitive::str, usize); 12] = [
// Unsigned
("u8", 0),
("u16", 0),
@ -56,15 +56,19 @@ pub const BASE_TYPES: [(&'static std::primitive::str, usize); 11] = [
("f64", 0),
// Other
("String", 0),
("Result", 2),
// FIXME(@soispha): These work, but the generated code is not really ideal <2024-03-26>
// ("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); 3] =
[header!("errno.h"), header!("string.h"), header!("vec.h")];
pub const C_TYPE_HEADER: [(&'static std::primitive::str, &'static std::primitive::str); 4] = [
header!("errno.h"),
header!("string.h"),
header!("vec.h"),
header!("result.h"),
];
pub fn header_names() -> std::string::String {
C_TYPE_HEADER

View File

@ -129,121 +129,93 @@ pub extern "C" fn string_free(ptr: *const c_char) {
CString::drop_ptr(ptr);
}
impl<T: Convertible> Convertible for Option<T> {
type Ptr = *const <T as Convertible>::Ptr;
// impl<T: Convertible> Convertible for Option<T> {
// type Ptr = *const <T as Convertible>::Ptr;
//
// fn into_ptr(self) -> Self::Ptr {
// if let Some(inner) = self {
// &inner.into_ptr()
// } else {
// ptr::null()
// }
// }
//
// fn from_ptr(_: Self::Ptr) -> Result<Self, error::TypeConversionError> {
// warn!(
// "
// 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<T>`)
// "
// );
// Ok(Option::default())
// }
// }
fn into_ptr(self) -> Self::Ptr {
if let Some(inner) = self {
&inner.into_ptr()
} else {
ptr::null()
}
}
fn from_ptr(_: Self::Ptr) -> Result<Self, error::TypeConversionError> {
warn!(
"
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<T>`)
"
);
Ok(Option::default())
}
}
impl<T: Convertible, E: Error> Convertible for Result<T, E> {
type Ptr = *const <T as Convertible>::Ptr;
fn into_ptr(self) -> Self::Ptr {
match self {
Ok(ok) => &ok.into_ptr(),
Err(err) => {
errno::set(TypeConversionError::ResultWasErr {
original_message: err.to_string(),
});
ptr::null()
}
}
}
fn from_ptr(_: Self::Ptr) -> Result<Self, error::TypeConversionError> {
warn!(
"
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<T>`)
"
);
todo!()
// Ok(Result::Ok(T::default()))
}
}
impl<T: Convertible> Convertible for Vec<T> {
type Ptr = crate::types::Vec<<T as Convertible>::Ptr>;
fn into_ptr(self) -> Self::Ptr {
let data_vec: Vec<_> = self.into_iter().map(|val| val.into_ptr()).collect();
let data_vec = ManuallyDrop::new(data_vec);
Self::Ptr {
data: data_vec.as_ptr(),
length: data_vec.len(),
capacity: data_vec.capacity(),
}
}
fn from_ptr(ptr: Self::Ptr) -> Result<Self, error::TypeConversionError> {
if ptr.data.is_null() {
return Err(TypeConversionError::NullPointer);
}
// TODO(@soispha): Add this, when this feature is stable <2024-02-17>
// if !ptr.data.is_aligned() {
// return Err(TypeConversionError::NotAligned);
// }
let base_vec = unsafe {
// 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 as Convertible>::Ptr,
ptr.length,
ptr.capacity,
)
};
let vec: Vec<_> = base_vec
.into_iter()
.map(|val| <T as Convertible>::from_ptr(val))
.collect::<Result<Vec<_>, _>>()?;
Ok(vec)
}
}
macro_rules! make_vec_free {
($value:ident, $name:ident) => {
#[no_mangle]
pub extern "C" fn $name(ptr: crate::types::Vec<<$value as Convertible>::Ptr>) {
Vec::<$value>::drop_ptr(ptr);
}
};
}
// Unsigned
make_vec_free!(u8, vec_free_u8);
make_vec_free!(u16, vec_free_u16);
make_vec_free!(u32, vec_free_u32);
make_vec_free!(u64, vec_free_u64);
// Signed
make_vec_free!(i8, vec_free_i8);
make_vec_free!(i16, vec_free_i16);
make_vec_free!(i32, vec_free_i32);
make_vec_free!(i64, vec_free_i64);
// Float
make_vec_free!(f32, vec_free_f32);
make_vec_free!(f64, vec_free_f64);
// Other
make_vec_free!(String, vec_free_String);
// impl<T: Convertible> Convertible for Vec<T> {
// type Ptr = crate::types::Vec<<T as Convertible>::Ptr>;
//
// fn into_ptr(self) -> Self::Ptr {
// let data_vec: Vec<_> = self.into_iter().map(|val| val.into_ptr()).collect();
// let data_vec = ManuallyDrop::new(data_vec);
// Self::Ptr {
// data: data_vec.as_ptr(),
// length: data_vec.len(),
// capacity: data_vec.capacity(),
// }
// }
//
// fn from_ptr(ptr: Self::Ptr) -> Result<Self, error::TypeConversionError> {
// if ptr.data.is_null() {
// return Err(TypeConversionError::NullPointer);
// }
// // TODO(@soispha): Add this, when this feature is stable <2024-02-17>
// // if !ptr.data.is_aligned() {
// // return Err(TypeConversionError::NotAligned);
// // }
// let base_vec = unsafe {
// // 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 as Convertible>::Ptr,
// ptr.length,
// ptr.capacity,
// )
// };
//
// let vec: Vec<_> = base_vec
// .into_iter()
// .map(|val| <T as Convertible>::from_ptr(val))
// .collect::<Result<Vec<_>, _>>()?;
// Ok(vec)
// }
// }
//
// macro_rules! make_vec_free {
// ($value:ident, $name:ident) => {
// #[no_mangle]
// pub extern "C" fn $name(ptr: crate::types::Vec<<$value as Convertible>::Ptr>) {
// Vec::<$value>::drop_ptr(ptr);
// }
// };
// }
//
// // Unsigned
// make_vec_free!(u8, vec_free_u8);
// make_vec_free!(u16, vec_free_u16);
// make_vec_free!(u32, vec_free_u32);
// make_vec_free!(u64, vec_free_u64);
// // Signed
// make_vec_free!(i8, vec_free_i8);
// make_vec_free!(i16, vec_free_i16);
// make_vec_free!(i32, vec_free_i32);
// make_vec_free!(i64, vec_free_i64);
// // Float
// make_vec_free!(f32, vec_free_f32);
// make_vec_free!(f64, vec_free_f64);
// // Other
// make_vec_free!(String, vec_free_String);
// FIXME(@soispha): Find a way to support these <2024-02-27>
// make_vec_free!(Option<?>, vec_free_Option);

View File

@ -23,24 +23,30 @@
// NOTE(@soispha): All types specified here *MUST* be include in the BASE_TYPES constant, otherwise
// they are not usable from Trixy code <2023-12-25>
// NOTE(@soispha): All types added here must also have an added free impl for the vector type (./traits/convert_trait.rs) <2024-02-27>
use std::ffi::c_char;
#[derive(Debug, Clone)]
#[repr(C)]
pub struct String(pub(crate) *const c_char);
/// The tag used to tag the c result Enumerations
#[derive(Debug)]
#[repr(C)]
pub struct Vec<T> {
/// You should cast this value to it's supposed type (readable from the Trixy api definition
/// file)
pub(crate) data: *const T,
pub(crate) length: usize,
pub(crate) capacity: usize,
pub enum ResultTag {
Ok,
Err,
}
// #[derive(Debug)]
// #[repr(C)]
// pub struct Vec<T> {
// /// You should cast this value to it's supposed type (readable from the Trixy api definition
// /// file)
// pub(crate) data: *const T,
// pub(crate) length: usize,
// pub(crate) capacity: usize,
// }
// Unsigned
pub use std::primitive::u16;
pub use std::primitive::u32;