From 268f1b4ff8b63a6245ef3a5b5b45b4ad222f4ff6 Mon Sep 17 00:00:00 2001 From: lifning <> Date: Sun, 17 Oct 2021 21:46:12 -0700 Subject: [PATCH] add scary-looking-but-safe dynamic downcast for a component_mut accessor --- ferretro_components/src/base/mod.rs | 36 +++++++++++++++++++++++------ ferretro_components/src/lib.rs | 2 +- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/ferretro_components/src/base/mod.rs b/ferretro_components/src/base/mod.rs index 9d68197..f464c2c 100644 --- a/ferretro_components/src/base/mod.rs +++ b/ferretro_components/src/base/mod.rs @@ -1,5 +1,6 @@ use crate::prelude::*; use ferretro_base::retro::ffi::*; +use std::any::{Any, TypeId}; use std::os::raw::c_uint; use std::path::{PathBuf, Path}; use std::pin::Pin; @@ -9,7 +10,8 @@ pub struct RetroComponentBase { retro: LibretroWrapper, libretro_path: PathBuf, // TODO: control when things get added to this with lifetime constraints defined by implementers - components: Vec>, + components: Vec>>, + component_ptrs: Vec<(TypeId, *mut ())>, // replaying env calls for late-added components cached_rom_path: Option, @@ -25,6 +27,9 @@ pub struct RetroComponentBase { cached_geometry: Option, } +#[derive(Copy, Clone, Debug)] +pub struct RetroComponentId(usize); + // TODO: replace with std::ops::ControlFlow when it becomes stable pub enum ControlFlow { Continue, @@ -35,7 +40,7 @@ pub type Result = std::result::Result>; #[rustfmt::skip] #[allow(unused_variables)] -pub trait RetroComponent: RetroCallbacks { +pub trait RetroComponent: RetroCallbacks + Any { fn pre_init(&mut self, retro: &mut LibretroWrapper) -> Result<()> { Ok(()) } fn post_init(&mut self, retro: &mut LibretroWrapper) -> Result<()> { Ok(()) } fn pre_load_game(&mut self, retro: &mut LibretroWrapper, rom: &Path) -> Result<()> { Ok(()) } @@ -55,6 +60,7 @@ impl RetroComponentBase { retro, libretro_path: core_path.as_ref().to_path_buf(), components: Vec::new(), + component_ptrs: Vec::new(), cached_rom_path: None, cached_pixel_format: None, cached_input_descriptors: None, @@ -87,11 +93,13 @@ impl RetroComponentBase { Ok(()) } - pub fn register_component(&mut self, comp: T) -> Option<()> // TODO: Result - where T: RetroComponent + pub fn register_component(&mut self, comp: T) -> Result + where T: RetroComponent + Any { // TODO: match comp.schedule { BeforeInit, BeforeLoad, BeforeFirstRun, Anytime } - let mut comp = Box::new(comp); + let comp_type = comp.type_id(); + let mut comp = Box::pin(comp); + let comp_ptr = comp.as_mut().get_mut() as *mut T; if let Some(cached) = &self.cached_pixel_format { if let Some(false) = comp.set_pixel_format(*cached) { // TODO: error, and propagate this pattern downward @@ -126,12 +134,26 @@ impl RetroComponentBase { } if let Some(cached) = &self.cached_rom_path { - comp.post_load_game(&mut self.retro, &cached); + comp.post_load_game(&mut self.retro, &cached)?; } self.components.push(comp); + self.component_ptrs.push((comp_type, comp_ptr as *mut ())); - Some(()) + Ok(RetroComponentId(self.components.len() - 1)) + } + + pub fn component_mut(&mut self, id: RetroComponentId) -> Result<&mut T> { + let (comp_type, comp_ptr) = self.component_ptrs.get(id.0) + .ok_or_else(|| format!("Invalid ID given to component_mut: {:?}", id))?; + if *comp_type == TypeId::of::() { + Ok(unsafe { &mut *(*comp_ptr as *mut T) }) + } else { + Err(format!( + "Invalid downcast for {:?}: {:?} != {:?}", + id, comp_type, TypeId::of::() + ).into()) + } } pub fn load_game(&mut self, rom: impl AsRef) -> Result<()> { diff --git a/ferretro_components/src/lib.rs b/ferretro_components/src/lib.rs index 87754d8..170ce1d 100644 --- a/ferretro_components/src/lib.rs +++ b/ferretro_components/src/lib.rs @@ -2,7 +2,7 @@ pub mod provided; pub mod base; pub mod prelude { - pub use crate::base::{RetroComponent, RetroComponentBase}; + pub use crate::base::{RetroComponent, RetroComponentBase, RetroComponentId}; pub use ferretro_base::retro::constants::*; pub use ferretro_base::retro::wrapped_types::*; pub use ferretro_base::retro::wrapper::{RetroCallbacks, LibretroWrapper, LibretroWrapperAccess};