-
Notifications
You must be signed in to change notification settings - Fork 190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switch from impl ExtendsC for E
to impl ExtendableFrom<E> for C
#879
Comments
This is a major drawback:
|
Is this (and this issue in general) implying that we must swap around the trait to make this work? Or specifically to have /// Implement this for extension struct `E` when it is allowed in the base struct `B` (i.e. `E` has `structextends="B"` in `vk.xml`).
pub trait StructExtends<B>: TaggedStructure {}
impl StructExtends<vk::DeviceQueueCreateInfo<'_>>
for vk::DeviceQueueGlobalPriorityCreateInfoKHR<'_>
{
}
/// Structures implementing this trait are layout-compatible with [`vk::BaseInStructure`] and
/// [`vk::BaseOutStructure`]. Such structures have an `s_type` field indicating its type, which
/// must always match the value of [`TaggedStructure::STRUCTURE_TYPE`].
pub unsafe trait TaggedStructure: Sized {
const STRUCTURE_TYPE: vk::StructureType;
fn push<T: StructExtends<Self>>(mut self, next: &mut T) -> Self {
// XXX: UB Risk? https://github.com/ash-rs/ash/pull/953#issuecomment-2521132802
// SAFETY: All implementors of `TaggedStructure` are required to have the `BaseOutStructure` layout
let slf = unsafe { &mut *<*mut _>::cast::<vk::BaseOutStructure<'_>>(&mut self) };
let next = unsafe { &mut *<*mut _>::cast::<vk::BaseOutStructure<'_>>(next) };
assert!(
next.p_next.is_null(),
"push() expects a struct without an existing p_next pointer chain (equal to NULL)"
);
next.p_next = slf.p_next;
slf.p_next = next;
self
}
}
#[test]
fn possible() {
let x = vk::DeviceQueueCreateInfo::default();
let mut y = vk::DeviceQueueGlobalPriorityCreateInfoKHR::default();
x.push(&mut y);
// y.push(&mut x); // Obviously invalid
} Regardless, this violates both points by @Ralith (despite me giving a lot of value to the former, but not much to the latter). If we decide to reduce these functions via |
Taking advantage of the existing I do still feel that traits can be a bit awkward, but we should balance that against the duplicate generated code and the fact that we've already embraced traits requiring import with There's a compromise solution, too: offer a top-level function |
Thanks for the insights! Would appreciate a suggestion for renaming a Such a Finally, notice that we have a |
I'm not sure there's a pithy term Vulkan uses here (relevant docs).
The concise name is intended to support qualified use. It'd be a bit ambiguous if imported.
IMO glob imports (and the preludes that promote them) are an anti-pattern due to readability and non-semver breakage hazard. I think we should ditch those modules entirely; they're mostly vestigal anyway. Let's export public helpers from the top-level |
Here I use
C
for "carrier" andE
for "element". A carrier is the struct that gets consumed by Vulkan API functions, and is the head of the pointer chain. An element is the opposite, just a part of the pointer chain.The elements a carrier may be extended by is defined by the Vulkan registry. We leverage this information to provide a compile-time safe API, by generating a
push_next
builder function for every carrier which only takes elements that implementExtendsC
(a trait only implemented for valid elements).In erupt, I switched this around: The trait is now implemented for the carriers, and the elements are in the generic parameter position. This means that the
push_next
function can now become a trait function with a default implementation, which works everywhere.See the
ExtendableFrom
trait from erupt.Side note: ash generates a separate trait for every carrier struct, where it could use generics (
Extends<C>
) instead.The text was updated successfully, but these errors were encountered: