Last active
August 28, 2023 17:59
-
-
Save EHfive/1843fe96a0b0c85e59bc057e25392631 to your computer and use it in GitHub Desktop.
Rust offset_of!() and container_of!() macros utilizing core::ptr::addr_of!()
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Macros | |
*/ | |
macro_rules! offset_of { | |
($Container:ty, $($fields:tt).+) => {{ | |
// avoid dereferencing null pointer | |
let container = ::core::mem::align_of::<$Container>() as *const $Container; | |
let member = ::core::ptr::addr_of!((*container).$($fields).+); | |
member.cast::<u8>().offset_from(container.cast::<u8>()) as usize | |
}}; | |
} | |
macro_rules! container_of { | |
($ptr:expr, $Container:ty, $($fields:tt).+) => {{ | |
let container = ::core::mem::align_of::<$Container>() as *const $Container; | |
let member = ::core::ptr::addr_of!((*container).$($fields).+); | |
if false { | |
// static type check | |
let _ = member == $ptr; | |
} | |
let offset = container.cast::<u8>().offset_from(member.cast::<u8>()); | |
($ptr.cast::<u8>()) | |
.offset(offset) | |
.cast::<$Container>() | |
}}; | |
} | |
/** | |
* Example | |
*/ | |
mod lib_x { | |
#[repr(C)] | |
#[derive(Default)] | |
pub struct Abi { | |
foo: u8, | |
bar: u8, | |
} | |
pub unsafe fn invoke_callback(this: *mut Abi, cb: unsafe fn(*mut Abi)) { | |
(*this).foo = 1; | |
cb(this); | |
} | |
} | |
const SIGNATURE: u32 = 0xdeadbeef; | |
#[repr(C)] | |
#[derive(Default)] | |
struct UserData { | |
sig: u32, | |
field_abi: lib_x::Abi, | |
tuple: (u8, u8), | |
callback_count: u32, | |
} | |
unsafe fn callback(this: *mut lib_x::Abi) { | |
let user_data = &mut *container_of!(this, UserData, field_abi); | |
assert_eq!(SIGNATURE, user_data.sig); | |
user_data.callback_count += 1; | |
} | |
fn main() { | |
unsafe { | |
let mut user_data = UserData { | |
sig: SIGNATURE, | |
..UserData::default() | |
}; | |
assert_eq!(4, offset_of!(UserData, field_abi)); | |
assert_eq!(7, offset_of!(UserData, tuple.1)); | |
lib_x::invoke_callback( | |
core::ptr::addr_of_mut!(user_data.field_abi), | |
callback, // | |
); | |
assert_eq!(1, user_data.callback_count); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment