Skip to content

Commit

Permalink
Allow implement_* macros to take a struct with a lifetime parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
tomaka committed Jul 10, 2015
1 parent 83c2c82 commit ae264fc
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

- Added the `CapabilitiesSource` trait implemented automatically on all types that implement `Facade`.
- Added `is_supported` to `IndexType`, `Index`, `PrimitiveType`, `Attribute` and `AttributeType`.
- The `implement_buffer_content!` and `implement_block_layout!` macros can now take a struct with a lifetime parameter by passing `Foo<'a>` instead of `Foo`.

## Version 0.7.0 (2015-07-08)

Expand Down
170 changes: 170 additions & 0 deletions src/macros.rs
Expand Up @@ -227,6 +227,76 @@ macro_rules! implement_buffer_content {
($struct_name:ident,) => (
implement_buffer_content!($struct_name);
);

// TODO: this is a copy-paste of the code above, but for structs that have a lifetime parameter
// (https://users.rust-lang.org/t/im-using-a-custom-derive-like-with-macros-how-do-i-handle-lifetimes/2058)
($struct_name:ident <$t1:tt>) => (
unsafe impl<'b> $crate::buffer::Content for $struct_name<'b> {
type Owned = Box<$struct_name<'b>>;

fn read<F, E>(size: usize, f: F) -> Result<Box<$struct_name<'b>>, E>
where F: FnOnce(&mut $struct_name<'b>) -> Result<(), E>
{
use std::mem;

assert!(<$struct_name as $crate::buffer::Content>::is_size_suitable(size));

let mut storage: Vec<u8> = Vec::with_capacity(size);
unsafe { storage.set_len(size) };
let storage = storage.into_boxed_slice();
let mut storage: Box<$struct_name> = unsafe { mem::transmute(storage) };

try!(f(&mut storage));
Ok(storage)
}

fn get_elements_size() -> usize {
use std::mem;

let fake_ptr: &$struct_name = unsafe { mem::transmute((0usize, 0usize)) };
mem::size_of_val(fake_ptr)
}

fn to_void_ptr(&self) -> *const () {
use std::mem;
let (ptr, _): (*const (), usize) = unsafe { mem::transmute(self) };
ptr
}

fn ref_from_ptr<'a>(ptr: *mut (), size: usize) -> Option<*mut $struct_name<'b>> {
use std::mem;

let fake_ptr: &$struct_name = unsafe { mem::transmute((0usize, 0usize)) };
let min_size = mem::size_of_val(fake_ptr);

let fake_ptr: &$struct_name = unsafe { mem::transmute((0usize, 1usize)) };
let step = mem::size_of_val(fake_ptr) - min_size;

if size < min_size {
return None;
}

let variadic = size - min_size;
if variadic % step != 0 {
return None;
}

Some(unsafe { mem::transmute((ptr, (variadic / step) as usize)) })
}

fn is_size_suitable(size: usize) -> bool {
use std::mem;

let fake_ptr: &$struct_name = unsafe { mem::transmute((0usize, 0usize)) };
let min_size = mem::size_of_val(fake_ptr);

let fake_ptr: &$struct_name = unsafe { mem::transmute((0usize, 1usize)) };
let step = mem::size_of_val(fake_ptr) - min_size;

size > min_size && (size - min_size) % step == 0
}
}
);
}

/// Implements the `glium::uniforms::UniformBlock` trait for the given type.
Expand Down Expand Up @@ -351,6 +421,106 @@ macro_rules! implement_uniform_block {
($struct_name:ident, $($field_name:ident),+,) => (
implement_uniform_block!($struct_name, $($field_name),+);
);


// TODO: this is a copy-paste of the code above, but with a lifetime parameter
// (https://users.rust-lang.org/t/im-using-a-custom-derive-like-with-macros-how-do-i-handle-lifetimes/2058)
($struct_name:ident<$l:tt>, $($field_name:ident),+) => (
impl<'a> $crate::uniforms::UniformBlock for $struct_name<'a> {
fn matches(layout: &$crate::program::BlockLayout, base_offset: usize)
-> Result<(), $crate::uniforms::LayoutMismatchError>
{
use std::mem;
use $crate::program::BlockLayout;
use $crate::uniforms::LayoutMismatchError;

if let &BlockLayout::Struct { ref members } = layout {
// checking that each member exists in the input struct
for &(ref name, _) in members {
if $(name != stringify!($field_name) &&)+ true {
return Err(LayoutMismatchError::MissingField {
name: name.clone(),
});
}
}

fn matches_from_ty<T: $crate::uniforms::UniformBlock + ?Sized>(_: &T,
layout: &$crate::program::BlockLayout, base_offset: usize)
-> Result<(), $crate::uniforms::LayoutMismatchError>
{
<T as $crate::uniforms::UniformBlock>::matches(layout, base_offset)
}

// checking that each field of the input struct is correct in the reflection
$(
let reflected_ty = members.iter().find(|&&(ref name, _)| {
name == stringify!($field_name)
});
let reflected_ty = match reflected_ty {
Some(t) => &t.1,
None => return Err(LayoutMismatchError::MissingField {
name: stringify!($field_name).to_string(),
})
};

let input_offset = {
let dummy: &$struct_name = unsafe { mem::zeroed() };
let dummy_field: *const _ = &dummy.$field_name;
dummy_field as *const () as usize
};

let dummy: &$struct_name = unsafe { mem::uninitialized() };

match matches_from_ty(&dummy.$field_name, reflected_ty, input_offset) {
Ok(_) => (),
Err(e) => return Err(LayoutMismatchError::MemberMismatch {
member: stringify!($field_name).to_string(),
err: Box::new(e),
})
};
)+

Ok(())

} else {
Err(LayoutMismatchError::LayoutMismatch {
expected: layout.clone(),
obtained: <Self as $crate::uniforms::UniformBlock>::build_layout(base_offset),
})
}
}

fn build_layout(base_offset: usize) -> $crate::program::BlockLayout {
use std::mem;
use $crate::program::BlockLayout;

fn layout_from_ty<T: $crate::uniforms::UniformBlock + ?Sized>(_: &T, base_offset: usize)
-> BlockLayout
{
<T as $crate::uniforms::UniformBlock>::build_layout(base_offset)
}

let dummy: &$struct_name = unsafe { mem::zeroed() };

BlockLayout::Struct {
members: vec![
$(
(
stringify!($field_name).to_string(),
{
let offset = {
let dummy_field: *const _ = &dummy.$field_name;
dummy_field as *const () as usize
};
layout_from_ty(&dummy.$field_name, offset + base_offset)
}
),
)+
],
}
}
}
);
}

/// Builds a program depending on the GLSL version supported by the backend.
Expand Down

0 comments on commit ae264fc

Please sign in to comment.