Skip to content

Commit 5bdf052

Browse files
committed
Allow specifing an expected type for field_offset!
Add detailed documentation and doc-tests
1 parent 86ea82c commit 5bdf052

File tree

2 files changed

+46
-3
lines changed

2 files changed

+46
-3
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//! I'd be happy to add more features as long as they align with the general philosophy
1111
//! of compile-time reflection.
1212
#![deny(missing_docs)]
13+
#![deny(unused_features, stable_features)]
1314
#![feature(
1415
const_panic, const_option, // We use Option::unwrap
1516
const_fn_fn_ptr_basics, // We use PhantomData<fn() -> T>

src/macros.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
macro_rules! define_extern_type {
66
($target:ident) => ($crate::define_extern_type!($target => $target););
77
($target:ty => $defined_path:path) => {
8-
unsafe impl ::static_reflect::StaticReflect for $target {
8+
unsafe impl $crate::StaticReflect for $target {
99
const TYPE_INFO: $crate::TypeInfo<'static> = $crate::TypeInfo::Extern {
1010
name: stringify!($defined_path)
1111
};
@@ -14,13 +14,55 @@ macro_rules! define_extern_type {
1414
}
1515

1616
/// Get the integer offset of the specified field
17+
///
18+
/// This is only well defined for `#[repr(C)]` types,
19+
/// since `#[repr(Rust)]` types don't have a well-defined layout.
20+
///
21+
/// ## Examples
22+
/// ````
23+
/// # use static_reflect::field_offset;
24+
/// # #[repr(C)]
25+
/// # struct Nested {
26+
/// # nested: u32
27+
/// # }
28+
/// #[repr(C)]
29+
/// struct Example {
30+
/// first: u32,
31+
/// second: u8,
32+
/// third: Nested,
33+
/// }
34+
/// assert_eq!(field_offset!(Example, first), 0);
35+
/// // You may specify the expected type by suffixing with `as $expected_type`
36+
/// assert_eq!(field_offset!(Example, second as u8), 4);
37+
/// // Fields can be arbitrarily nested
38+
/// assert_eq!(field_offset!(Example, third.nested as u32), 8);
39+
/// ````
40+
///
41+
/// ## Const eval
42+
/// Assuming you specify the appropriate feature flags,
43+
/// this macro can also be used in a const-eval context.
44+
/// ````
45+
/// #![feature(const_raw_ptr_deref, const_maybe_uninit_as_ptr, const_ptr_offset_from)]
46+
/// # #![deny(unused_features, stable_features)]
47+
/// # use static_reflect::field_offset;
48+
/// struct Example {
49+
/// first: u32,
50+
/// second: u8,
51+
/// }
52+
/// const FIRST_OFFSET: usize = field_offset!(Example, first as u32);
53+
/// assert_eq!(FIRST_OFFSET, 0);
54+
/// const SECOND_OFFSET: usize = field_offset!(Example, second as u8);
55+
/// assert_eq!(SECOND_OFFSET, 4);
56+
/// ````
1757
#[macro_export]
1858
macro_rules! field_offset {
19-
($target:path, $($field:ident),+) => {
59+
($target:path, $($field:ident).*) => (field_offset!($target, $($field).* as _));
60+
($target:path, $($field:ident).+ as $expected_type:ty) => {
2061
unsafe {
2162
let uninit = core::mem::MaybeUninit::<$target>::uninit();
2263
let base = uninit.as_ptr();
23-
(core::ptr::addr_of!((*base)$(.$field)*).cast::<u8>().offset_from(base as *const u8) as usize)
64+
let ptr: *const $expected_type = core::ptr::addr_of!((*base)$(.$field)*);
65+
ptr.cast::<u8>().offset_from(base as *const u8) as usize
2466
}
2567
}
2668
}

0 commit comments

Comments
 (0)