mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-30 13:51:13 +00:00
Work on const field offset crate to give type safe offsets
This commit is contained in:
parent
aff3d7e14b
commit
751a3fbe59
9 changed files with 399 additions and 107 deletions
|
@ -2,7 +2,7 @@ pub use sixtyfps_rs_macro::sixtyfps;
|
||||||
|
|
||||||
/// internal re_exports used by the macro generated
|
/// internal re_exports used by the macro generated
|
||||||
pub mod re_exports {
|
pub mod re_exports {
|
||||||
pub use const_field_offset::FieldOffsets;
|
pub use const_field_offset::{self, FieldOffsets};
|
||||||
pub use corelib::abi::datastructures::{Component, ComponentTO, ComponentVTable, ItemTreeNode};
|
pub use corelib::abi::datastructures::{Component, ComponentTO, ComponentVTable, ItemTreeNode};
|
||||||
pub use corelib::abi::primitives::{Image, ImageVTable, Rectangle, RectangleVTable};
|
pub use corelib::abi::primitives::{Image, ImageVTable, Rectangle, RectangleVTable};
|
||||||
pub use corelib::ComponentVTable_static;
|
pub use corelib::ComponentVTable_static;
|
||||||
|
|
|
@ -113,7 +113,7 @@ pub fn sixtyfps(stream: TokenStream) -> TokenStream {
|
||||||
let children_count = item.children.len() as u32;
|
let children_count = item.children.len() as u32;
|
||||||
item_tree_array.push(quote!(
|
item_tree_array.push(quote!(
|
||||||
sixtyfps::re_exports::ItemTreeNode::Item{
|
sixtyfps::re_exports::ItemTreeNode::Item{
|
||||||
offset: #component_id::field_offsets().#field_name as isize,
|
offset: #component_id::field_offsets().#field_name.get_byte_offset() as isize,
|
||||||
vtable: &#vtable as *const _,
|
vtable: &#vtable as *const _,
|
||||||
chilren_count: #children_count,
|
chilren_count: #children_count,
|
||||||
children_index: #children_index,
|
children_index: #children_index,
|
||||||
|
@ -141,6 +141,7 @@ pub fn sixtyfps(stream: TokenStream) -> TokenStream {
|
||||||
let item_tree_array_len = item_tree_array.len();
|
let item_tree_array_len = item_tree_array.len();
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
|
use sixtyfps::re_exports::const_field_offset;
|
||||||
#[derive(sixtyfps::re_exports::FieldOffsets)]
|
#[derive(sixtyfps::re_exports::FieldOffsets)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct #component_id {
|
struct #component_id {
|
||||||
|
|
|
@ -4,14 +4,8 @@ version = "0.1.0"
|
||||||
authors = ["Sixty FPS <info@sixtyfps.io>"]
|
authors = ["Sixty FPS <info@sixtyfps.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
syn = { version = "1.0", features = ["derive"] }
|
const-field-offset-macro = { path = "./macro" }
|
||||||
quote = "1.0"
|
|
||||||
proc-macro2 = "1.0"
|
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
memoffset = "0.5.4"
|
memoffset = "0.5.4"
|
||||||
|
|
18
helper_crates/const-field-offset/macro/Cargo.toml
Normal file
18
helper_crates/const-field-offset/macro/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "const-field-offset-macro"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Sixty FPS <info@sixtyfps.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "macro.rs"
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
syn = { version = "1.0", features = ["derive"] }
|
||||||
|
quote = "1.0"
|
||||||
|
proc-macro2 = "1.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
memoffset = "0.5.4"
|
||||||
|
const-field-offset = { path="../" }
|
94
helper_crates/const-field-offset/macro/macro.rs
Normal file
94
helper_crates/const-field-offset/macro/macro.rs
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/*!
|
||||||
|
|
||||||
|
This crate allow to get the offset of a field of a structure in a const or static context.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use const_field_offset::FieldOffsets;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(FieldOffsets)]
|
||||||
|
struct Foo {
|
||||||
|
field_1 : u8,
|
||||||
|
field_2 : u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const FOO : usize = Foo::field_offsets().field_2.get_byte_offset();
|
||||||
|
assert_eq!(FOO, 4);
|
||||||
|
|
||||||
|
// This would not work on stable rust at the moment (rust 1.43)
|
||||||
|
// const FOO : usize = memofsets::offsetof!(Foo, field_2);
|
||||||
|
```
|
||||||
|
|
||||||
|
The macro FieldOffsets adds a `const fn field_offsets()` associated function to the struct, that
|
||||||
|
returns an object which has a bunch of usize fields with the same name as the fields of the
|
||||||
|
original struct.
|
||||||
|
|
||||||
|
## limitations
|
||||||
|
|
||||||
|
Only work with named #[repr(C)] structures.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{parse_macro_input, DeriveInput};
|
||||||
|
|
||||||
|
#[proc_macro_derive(FieldOffsets)]
|
||||||
|
pub fn const_field_offset(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
if !input.attrs.iter().any(|a| {
|
||||||
|
if let Some(i) = a.path.get_ident() {
|
||||||
|
i == "repr" && a.tokens.to_string() == "(C)"
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
return TokenStream::from(quote! {compile_error!{"Only work if repr(C)"}});
|
||||||
|
};
|
||||||
|
|
||||||
|
let struct_name = input.ident;
|
||||||
|
let field_struct_name = quote::format_ident!("{}FieldsOffsets", struct_name);
|
||||||
|
|
||||||
|
let (fields, types, vis) = if let syn::Data::Struct(s) = &input.data {
|
||||||
|
if let syn::Fields::Named(n) = &s.fields {
|
||||||
|
let (f, tv): (Vec<_>, Vec<_>) =
|
||||||
|
n.named.iter().map(|f| (&f.ident, (&f.ty, &f.vis))).unzip();
|
||||||
|
let (t, v): (Vec<_>, Vec<_>) = tv.into_iter().unzip();
|
||||||
|
(f, t, v)
|
||||||
|
} else {
|
||||||
|
return TokenStream::from(quote! {compile_error!{"Only work for named fields"}});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return TokenStream::from(quote! {compile_error!("Only work for struct")});
|
||||||
|
};
|
||||||
|
|
||||||
|
let crate_ = quote!(const_field_offset);
|
||||||
|
|
||||||
|
// Build the output, possibly using quasi-quotation
|
||||||
|
let expanded = quote! {
|
||||||
|
pub struct #field_struct_name {
|
||||||
|
#(#vis #fields : #crate_::FieldOffset<#struct_name, #types>,)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl #struct_name {
|
||||||
|
pub const fn field_offsets() -> #field_struct_name {
|
||||||
|
let mut len = 0usize;
|
||||||
|
#field_struct_name {
|
||||||
|
#( #fields : {
|
||||||
|
let align = ::core::mem::align_of::<#types>();
|
||||||
|
// from Layout::padding_needed_for which is not yet stable
|
||||||
|
let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
|
||||||
|
len = len_rounded_up + ::core::mem::size_of::<#types>();
|
||||||
|
/// Safety: According to the rules of repr(C), this is the right offset
|
||||||
|
unsafe { #crate_::FieldOffset::<#struct_name, #types>::new_from_offset(len_rounded_up) }
|
||||||
|
}, )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hand the output tokens back to the compiler
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
|
@ -1,91 +1,274 @@
|
||||||
/*!
|
/*
|
||||||
|
The FieldOffster structure is forked from https://docs.rs/field-offset/0.3.0/src/field_offset/lib.rs.html
|
||||||
|
|
||||||
This crate allow to get the offset of a field of a structure in a const or static context.
|
The changes include:
|
||||||
|
- Only the FieldOffset structure was imported, not the macros
|
||||||
|
- re-export of the FieldOffsets derive macro
|
||||||
|
- add const in most method
|
||||||
|
|
||||||
```rust
|
(there is a `//###` comment in front of each change)
|
||||||
use const_field_offset::FieldOffsets;
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(FieldOffsets)]
|
|
||||||
struct Foo {
|
|
||||||
field_1 : u8,
|
|
||||||
field_2 : u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
const FOO : usize = Foo::field_offsets().field_2;
|
|
||||||
assert_eq!(FOO, 4);
|
|
||||||
|
|
||||||
// This would not work on stable rust at the moment (rust 1.43)
|
|
||||||
// const FOO : usize = memofsets::offsetof!(Foo, field_2);
|
|
||||||
```
|
|
||||||
|
|
||||||
The macro FieldOffsets adds a `const fn field_offsets()` associated function to the struct, that
|
|
||||||
returns an object which has a bunch of usize fields with the same name as the fields of the
|
|
||||||
original struct.
|
|
||||||
|
|
||||||
## limitations
|
|
||||||
|
|
||||||
Only work with named #[repr(C)] structures.
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern crate proc_macro;
|
use std::fmt;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::mem;
|
||||||
|
use std::ops::Add;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
#[doc(inline)]
|
||||||
use quote::quote;
|
pub use const_field_offset_macro::FieldOffsets;
|
||||||
use syn::{parse_macro_input, DeriveInput};
|
|
||||||
|
|
||||||
#[proc_macro_derive(FieldOffsets)]
|
/// Represents a pointer to a field of type `U` within the type `T`
|
||||||
pub fn const_field_offset(input: TokenStream) -> TokenStream {
|
#[repr(transparent)]
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
pub struct FieldOffset<T, U>(
|
||||||
|
/// Offset in bytes of the field within the struct
|
||||||
|
usize,
|
||||||
|
// ### Changed from Fn to fn to allow const
|
||||||
|
// it is fine to be fariant
|
||||||
|
PhantomData<(*const T, *const U)>,
|
||||||
|
);
|
||||||
|
|
||||||
if !input.attrs.iter().any(|a| {
|
impl<T, U> FieldOffset<T, U> {
|
||||||
if let Some(i) = a.path.get_ident() {
|
// Use MaybeUninit to get a fake T
|
||||||
i == "repr" && a.tokens.to_string() == "(C)"
|
#[cfg(fieldoffset_maybe_uninit)]
|
||||||
} else {
|
#[inline]
|
||||||
false
|
fn with_uninit_ptr<R, F: FnOnce(*const T) -> R>(f: F) -> R {
|
||||||
}
|
let uninit = mem::MaybeUninit::<T>::uninit();
|
||||||
}) {
|
f(uninit.as_ptr())
|
||||||
return TokenStream::from(quote! {compile_error!{"Only work if repr(C)"}});
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let struct_name = input.ident;
|
// Use a dangling pointer to get a fake T
|
||||||
let field_struct_name = quote::format_ident!("{}FieldsOffsets", struct_name);
|
#[cfg(not(fieldoffset_maybe_uninit))]
|
||||||
|
#[inline]
|
||||||
|
fn with_uninit_ptr<R, F: FnOnce(*const T) -> R>(f: F) -> R {
|
||||||
|
f(mem::align_of::<T>() as *const T)
|
||||||
|
}
|
||||||
|
|
||||||
let (fields, types) = if let syn::Data::Struct(s) = input.data {
|
/// Construct a field offset via a lambda which returns a reference
|
||||||
if let syn::Fields::Named(n) = s.fields {
|
/// to the field in question.
|
||||||
(
|
///
|
||||||
n.named.iter().map(|f| f.ident.clone()).collect::<Vec<_>>(),
|
/// # Safety
|
||||||
n.named.iter().map(|f| f.ty.clone()).collect::<Vec<_>>(),
|
///
|
||||||
)
|
/// The lambda *must not* dereference the provided pointer or access the
|
||||||
} else {
|
/// inner value in any way as it may point to uninitialized memory.
|
||||||
return TokenStream::from(quote! {compile_error!{"Only work for named fields"}});
|
///
|
||||||
}
|
/// For the returned `FieldOffset` to be safe to use, the returned pointer
|
||||||
} else {
|
/// must be valid for *any* instance of `T`. For example, returning a pointer
|
||||||
return TokenStream::from(quote! {compile_error!("Only work for struct")});
|
/// to a field from an enum with multiple variants will produce a `FieldOffset`
|
||||||
};
|
/// which is unsafe to use.
|
||||||
|
pub unsafe fn new<F: for<'a> FnOnce(*const T) -> *const U>(f: F) -> Self {
|
||||||
|
let offset = Self::with_uninit_ptr(|base_ptr| {
|
||||||
|
let field_ptr = f(base_ptr);
|
||||||
|
(field_ptr as usize).wrapping_sub(base_ptr as usize)
|
||||||
|
});
|
||||||
|
|
||||||
// Build the output, possibly using quasi-quotation
|
// Construct an instance using the offset
|
||||||
let expanded = quote! {
|
Self::new_from_offset(offset)
|
||||||
pub struct #field_struct_name {
|
}
|
||||||
#(pub #fields : usize,)*
|
/// Construct a field offset directly from a byte offset.
|
||||||
}
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// For the returned `FieldOffset` to be safe to use, the field offset
|
||||||
|
/// must be valid for *any* instance of `T`. For example, returning the offset
|
||||||
|
/// to a field from an enum with multiple variants will produce a `FieldOffset`
|
||||||
|
/// which is unsafe to use.
|
||||||
|
#[inline]
|
||||||
|
pub const unsafe fn new_from_offset(offset: usize) -> Self {
|
||||||
|
// ### made const so assert is not allowed
|
||||||
|
// Sanity check: ensure that the field offset plus the field size
|
||||||
|
// is no greater than the size of the containing struct. This is
|
||||||
|
// not sufficient to make the function *safe*, but it does catch
|
||||||
|
// obvious errors like returning a reference to a boxed value,
|
||||||
|
// which is owned by `T` and so has the correct lifetime, but is not
|
||||||
|
// actually a field.
|
||||||
|
//assert!(offset + mem::size_of::<U>() <= mem::size_of::<T>());
|
||||||
|
|
||||||
impl #struct_name {
|
FieldOffset(offset, PhantomData)
|
||||||
pub const fn field_offsets() -> #field_struct_name {
|
}
|
||||||
let mut len = 0usize;
|
|
||||||
#field_struct_name {
|
|
||||||
#( #fields : {
|
|
||||||
let align = ::core::mem::align_of::<#types>();
|
|
||||||
// from Layout::padding_needed_for whiwh was not yet stable
|
|
||||||
let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
|
|
||||||
len = len_rounded_up + ::core::mem::size_of::<#types>();
|
|
||||||
len_rounded_up
|
|
||||||
}, )*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hand the output tokens back to the compiler
|
// Methods for applying the pointer to member
|
||||||
TokenStream::from(expanded)
|
|
||||||
|
/// Apply the field offset to a native pointer.
|
||||||
|
#[inline]
|
||||||
|
pub fn apply_ptr(self, x: *const T) -> *const U {
|
||||||
|
((x as usize) + self.0) as *const U
|
||||||
|
}
|
||||||
|
/// Apply the field offset to a native mutable pointer.
|
||||||
|
#[inline]
|
||||||
|
pub fn apply_ptr_mut(self, x: *mut T) -> *mut U {
|
||||||
|
((x as usize) + self.0) as *mut U
|
||||||
|
}
|
||||||
|
/// Apply the field offset to a reference.
|
||||||
|
#[inline]
|
||||||
|
pub fn apply<'a>(self, x: &'a T) -> &'a U {
|
||||||
|
unsafe { &*self.apply_ptr(x) }
|
||||||
|
}
|
||||||
|
/// Apply the field offset to a mutable reference.
|
||||||
|
#[inline]
|
||||||
|
pub fn apply_mut<'a>(self, x: &'a mut T) -> &'a mut U {
|
||||||
|
unsafe { &mut *self.apply_ptr_mut(x) }
|
||||||
|
}
|
||||||
|
/// Get the raw byte offset for this field offset.
|
||||||
|
#[inline]
|
||||||
|
pub const fn get_byte_offset(self) -> usize {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods for unapplying the pointer to member
|
||||||
|
|
||||||
|
/// Unapply the field offset to a native pointer.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// *Warning: very unsafe!*
|
||||||
|
///
|
||||||
|
/// This applies a negative offset to a pointer. If the safety
|
||||||
|
/// implications of this are not already clear to you, then *do
|
||||||
|
/// not* use this method. Also be aware that Rust has stronger
|
||||||
|
/// aliasing rules than other languages, so it may be UB to
|
||||||
|
/// dereference the resulting pointer even if it points to a valid
|
||||||
|
/// location, due to the presence of other live references.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn unapply_ptr(self, x: *const U) -> *const T {
|
||||||
|
((x as usize) - self.0) as *const T
|
||||||
|
}
|
||||||
|
/// Unapply the field offset to a native mutable pointer.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// *Warning: very unsafe!*
|
||||||
|
///
|
||||||
|
/// This applies a negative offset to a pointer. If the safety
|
||||||
|
/// implications of this are not already clear to you, then *do
|
||||||
|
/// not* use this method. Also be aware that Rust has stronger
|
||||||
|
/// aliasing rules than other languages, so it may be UB to
|
||||||
|
/// dereference the resulting pointer even if it points to a valid
|
||||||
|
/// location, due to the presence of other live references.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn unapply_ptr_mut(self, x: *mut U) -> *mut T {
|
||||||
|
((x as usize) - self.0) as *mut T
|
||||||
|
}
|
||||||
|
/// Unapply the field offset to a reference.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// *Warning: very unsafe!*
|
||||||
|
///
|
||||||
|
/// This applies a negative offset to a reference. If the safety
|
||||||
|
/// implications of this are not already clear to you, then *do
|
||||||
|
/// not* use this method. Also be aware that Rust has stronger
|
||||||
|
/// aliasing rules than other languages, so this method may cause UB
|
||||||
|
/// even if the resulting reference points to a valid location, due
|
||||||
|
/// to the presence of other live references.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn unapply<'a>(self, x: &'a U) -> &'a T {
|
||||||
|
&*self.unapply_ptr(x)
|
||||||
|
}
|
||||||
|
/// Unapply the field offset to a mutable reference.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// *Warning: very unsafe!*
|
||||||
|
///
|
||||||
|
/// This applies a negative offset to a reference. If the safety
|
||||||
|
/// implications of this are not already clear to you, then *do
|
||||||
|
/// not* use this method. Also be aware that Rust has stronger
|
||||||
|
/// aliasing rules than other languages, so this method may cause UB
|
||||||
|
/// even if the resulting reference points to a valid location, due
|
||||||
|
/// to the presence of other live references.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn unapply_mut<'a>(self, x: &'a mut U) -> &'a mut T {
|
||||||
|
&mut *self.unapply_ptr_mut(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allow chaining pointer-to-members.
|
||||||
|
///
|
||||||
|
/// Applying the resulting field offset is equivalent to applying the first
|
||||||
|
/// field offset, then applying the second field offset.
|
||||||
|
///
|
||||||
|
/// The requirements on the generic type parameters ensure this is a safe operation.
|
||||||
|
impl<T, U, V> Add<FieldOffset<U, V>> for FieldOffset<T, U> {
|
||||||
|
type Output = FieldOffset<T, V>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn add(self, other: FieldOffset<U, V>) -> FieldOffset<T, V> {
|
||||||
|
FieldOffset(self.0 + other.0, PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The debug implementation prints the byte offset of the field in hexadecimal.
|
||||||
|
impl<T, U> fmt::Debug for FieldOffset<T, U> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
write!(f, "FieldOffset({:#x})", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> Copy for FieldOffset<T, U> {}
|
||||||
|
impl<T, U> Clone for FieldOffset<T, U> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate as const_field_offset;
|
||||||
|
// ### Structures were change to repr(c) and to inherit FieldOffsets
|
||||||
|
|
||||||
|
// Example structs
|
||||||
|
#[derive(Debug, FieldOffsets)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct Foo {
|
||||||
|
a: u32,
|
||||||
|
b: f64,
|
||||||
|
c: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FieldOffsets)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct Bar {
|
||||||
|
x: u32,
|
||||||
|
y: Foo,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_simple() {
|
||||||
|
// Get a pointer to `b` within `Foo`
|
||||||
|
let foo_b = Foo::field_offsets().b;
|
||||||
|
|
||||||
|
// Construct an example `Foo`
|
||||||
|
let mut x = Foo { a: 1, b: 2.0, c: false };
|
||||||
|
|
||||||
|
// Apply the pointer to get at `b` and read it
|
||||||
|
{
|
||||||
|
let y = foo_b.apply(&x);
|
||||||
|
assert!(*y == 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the pointer to get at `b` and mutate it
|
||||||
|
{
|
||||||
|
let y = foo_b.apply_mut(&mut x);
|
||||||
|
*y = 42.0;
|
||||||
|
}
|
||||||
|
assert!(x.b == 42.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nested() {
|
||||||
|
// Construct an example `Foo`
|
||||||
|
let mut x = Bar { x: 0, y: Foo { a: 1, b: 2.0, c: false } };
|
||||||
|
|
||||||
|
// Combine the pointer-to-members
|
||||||
|
let bar_y_b = Bar::field_offsets().y + Foo::field_offsets().b;
|
||||||
|
|
||||||
|
// Apply the pointer to get at `b` and mutate it
|
||||||
|
{
|
||||||
|
let y = bar_y_b.apply_mut(&mut x);
|
||||||
|
*y = 42.0;
|
||||||
|
}
|
||||||
|
assert!(x.y.b == 42.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,18 @@ struct MyStruct2 {
|
||||||
v: u32,
|
v: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
const XX_CONST: usize = MyStruct2::field_offsets().xx;
|
const XX_CONST: usize = MyStruct2::field_offsets().xx.get_byte_offset();
|
||||||
static D_STATIC: usize = MyStruct::field_offsets().d;
|
static D_STATIC: usize = MyStruct::field_offsets().d.get_byte_offset();
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() {
|
||||||
assert_eq!(offset_of!(MyStruct, a), MyStruct::field_offsets().a);
|
assert_eq!(offset_of!(MyStruct, a), MyStruct::field_offsets().a.get_byte_offset());
|
||||||
assert_eq!(offset_of!(MyStruct, b), MyStruct::field_offsets().b);
|
assert_eq!(offset_of!(MyStruct, b), MyStruct::field_offsets().b.get_byte_offset());
|
||||||
assert_eq!(offset_of!(MyStruct, c), MyStruct::field_offsets().c);
|
assert_eq!(offset_of!(MyStruct, c), MyStruct::field_offsets().c.get_byte_offset());
|
||||||
assert_eq!(offset_of!(MyStruct, d), MyStruct::field_offsets().d);
|
assert_eq!(offset_of!(MyStruct, d), MyStruct::field_offsets().d.get_byte_offset());
|
||||||
assert_eq!(offset_of!(MyStruct2, xx), MyStruct2::field_offsets().xx);
|
assert_eq!(offset_of!(MyStruct2, xx), MyStruct2::field_offsets().xx.get_byte_offset());
|
||||||
assert_eq!(offset_of!(MyStruct2, v), MyStruct2::field_offsets().v);
|
assert_eq!(offset_of!(MyStruct2, v), MyStruct2::field_offsets().v.get_byte_offset());
|
||||||
assert_eq!(offset_of!(MyStruct2, k), MyStruct2::field_offsets().k);
|
assert_eq!(offset_of!(MyStruct2, k), MyStruct2::field_offsets().k.get_byte_offset());
|
||||||
|
|
||||||
assert_eq!(XX_CONST, offset_of!(MyStruct2, xx));
|
assert_eq!(XX_CONST, offset_of!(MyStruct2, xx));
|
||||||
assert_eq!(D_STATIC, offset_of!(MyStruct, d));
|
assert_eq!(D_STATIC, offset_of!(MyStruct, d));
|
||||||
|
|
|
@ -33,7 +33,7 @@ impl Item for Rectangle {
|
||||||
|
|
||||||
impl ItemConsts for Rectangle {
|
impl ItemConsts for Rectangle {
|
||||||
const cached_rendering_data_offset: isize =
|
const cached_rendering_data_offset: isize =
|
||||||
Rectangle::field_offsets().cached_rendering_data as isize;
|
Rectangle::field_offsets().cached_rendering_data.get_byte_offset() as isize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: remove (or use the libc one)
|
// FIXME: remove (or use the libc one)
|
||||||
|
@ -80,7 +80,7 @@ impl Item for Image {
|
||||||
|
|
||||||
impl ItemConsts for Image {
|
impl ItemConsts for Image {
|
||||||
const cached_rendering_data_offset: isize =
|
const cached_rendering_data_offset: isize =
|
||||||
Image::field_offsets().cached_rendering_data as isize;
|
Image::field_offsets().cached_rendering_data.get_byte_offset() as isize;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
|
|
@ -98,17 +98,18 @@ fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
// FIXME: thus obviously is unsafe and not great
|
// FIXME: thus obviously is unsafe and not great
|
||||||
let mut rtti = HashMap::new();
|
let mut rtti = HashMap::new();
|
||||||
|
let offsets = Rectangle::field_offsets();
|
||||||
rtti.insert(
|
rtti.insert(
|
||||||
"Rectangle",
|
"Rectangle",
|
||||||
RuntimeTypeInfo {
|
RuntimeTypeInfo {
|
||||||
vtable: &corelib::abi::primitives::RectangleVTable as _,
|
vtable: &corelib::abi::primitives::RectangleVTable as _,
|
||||||
construct: construct::<Rectangle>,
|
construct: construct::<Rectangle>,
|
||||||
properties: [
|
properties: [
|
||||||
("x", (Rectangle::field_offsets().x, set_property::<f32> as _)),
|
("x", (offsets.x.get_byte_offset(), set_property::<f32> as _)),
|
||||||
("y", (Rectangle::field_offsets().y, set_property::<f32> as _)),
|
("y", (offsets.y.get_byte_offset(), set_property::<f32> as _)),
|
||||||
("width", (Rectangle::field_offsets().width, set_property::<f32> as _)),
|
("width", (offsets.width.get_byte_offset(), set_property::<f32> as _)),
|
||||||
("height", (Rectangle::field_offsets().height, set_property::<f32> as _)),
|
("height", (offsets.height.get_byte_offset(), set_property::<f32> as _)),
|
||||||
("color", (Rectangle::field_offsets().color, set_property::<u32> as _)),
|
("color", (offsets.color.get_byte_offset(), set_property::<u32> as _)),
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
|
@ -116,17 +117,18 @@ fn main() -> std::io::Result<()> {
|
||||||
size: std::mem::size_of::<Rectangle>(),
|
size: std::mem::size_of::<Rectangle>(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
let offsets = Image::field_offsets();
|
||||||
rtti.insert(
|
rtti.insert(
|
||||||
"Image",
|
"Image",
|
||||||
RuntimeTypeInfo {
|
RuntimeTypeInfo {
|
||||||
vtable: &corelib::abi::primitives::ImageVTable as _,
|
vtable: &corelib::abi::primitives::ImageVTable as _,
|
||||||
construct: construct::<Image>,
|
construct: construct::<Image>,
|
||||||
properties: [
|
properties: [
|
||||||
("x", (Image::field_offsets().x, set_property::<f32> as _)),
|
("x", (offsets.x.get_byte_offset(), set_property::<f32> as _)),
|
||||||
("y", (Image::field_offsets().y, set_property::<f32> as _)),
|
("y", (offsets.y.get_byte_offset(), set_property::<f32> as _)),
|
||||||
("width", (Image::field_offsets().width, set_property::<f32> as _)),
|
("width", (offsets.width.get_byte_offset(), set_property::<f32> as _)),
|
||||||
("height", (Image::field_offsets().height, set_property::<f32> as _)),
|
("height", (offsets.height.get_byte_offset(), set_property::<f32> as _)),
|
||||||
("source", (Image::field_offsets().source, set_property::<*const i8> as _)),
|
("source", (offsets.source.get_byte_offset(), set_property::<*const i8> as _)),
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue