通过特征对象创建回调



我不确定我到底需要什么,因此措辞有点模糊。

背景:我正在将一个C++PDP11模拟器移植到rust,现在我正在实现内存映射IO。每个设备都将自己注册为响应总线上的一组地址。每个设备实现都提供了一个setter和一个getter函数来调用为给定地址提供服务。一个设备可以提供多个。到目前为止我有这个。

use crate::common::*;
mod sysdev;
pub trait Device{
fn get_device_map(&self) -> Vec<DeviceMapping>;
fn reset(&mut self);
fn interrupt_ack(&mut self, slot: u32);
fn should_trace(&self) -> bool;
}
pub struct DeviceMapping {
pub addr: Word,
pub setter: fn(&mut dyn Device, val: Word),
pub getter: fn(&mut dyn Device) -> Word
}

正如您所看到的,设备必须实现get_device_map函数,该函数返回地址和回调列表。

我的第一台设备(一台非常简单的设备(

use crate::devices::*;
struct SysDevices {}
impl SysDevices{
pub fn get_psw(&mut self) -> Word{
42
}
pub fn set_psw(&mut self, val:Word){
}
}
impl Device for SysDevices{
fn get_device_map(&self) -> Vec<DeviceMapping>{
let psw = DeviceMapping {
addr: 0o177776,
getter: Self::get_psw,
setter: Self::set_psw
}
return vec![psw];
}
fn reset(&mut self){}
fn interrupt_ack(&mut self, slot:u32){}
fn should_trace(&self) -> bool{true}
}

这不会编译。

error[E0308]: mismatched types
--> srcdevicessysdev.rs:19:21
|
19 |             getter: Self::get_psw,
|                     ^^^^^^^^^^^^^ expected trait object `dyn devices::Device`, found struct `devices::sysdev::SysDevices`
|
= note: expected fn pointer `for<'r> fn(&'r mut (dyn devices::Device + 'r)) -> _`
found fn item `for<'r> fn(&'r mut devices::sysdev::SysDevices) -> _ {devices::sysdev::SysDevices::get_psw}`

这似乎是在说,我提供的是一个具体的对象,而不是一个特征对象。我少了什么铁锈魔法?也许setter和getter的定义是错误的,我说它们是接受Device对象的fn,这似乎是正确的。

编辑:添加了将这些结合在一起的百里香胶代码

总线的一个实例管理所有的设备并拥有内存。

pub struct Bus {
pub ram: Memory,
devices:Vec<Box<ConnectedDevice>>,
devmap:[u8;4096],
}
pub struct ConnectedDevice{
pub vector:Word,
pub device:Box<dyn Device>,
pub regmap:Vec<DeviceMapping>
}

devmap是一个大数组,在每个可能的地址都有一个索引,0表示没有设备,非零是连接设备的设备vec中的索引。(在中对零条目进行硬编码(

启动时,为每个设备调用attach_device

pub fn attach_device(&mut self, device:Box<dyn Device>) -> Result<(), Exception>{
let idx = self.devices.len() as u8;
let map = device.get_device_map();
let dev = Box::new(ConnectedDevice{
regmap:map,
vector:0,
device,

});
self.devices.push(dev);
for reg in &self.devices[idx as usize].regmap{
self.devmap[reg.addr as usize] = idx;
}
Ok(())
}

在运行时,当一个单词被读取时,我们转到这里(仍在公交车上(

pub fn get_word(&mut self, addr: Word) -> Result<Word, Exception> {
if addr > self.ram.size as u16 {
if addr >= DEVICE_BASE_ADDR {
let dev = self.get_device(addr)?;
let x = dev.regmap.iter().find(|x| x.addr == addr).unwrap();
let w = (x.getter)(&mut *dev.device);
Ok(w)
} else {
throw!(ExceptionType::NoRam)
}
} else {
Ok(self.ram.get_word(addr))
}
}
fn get_device(&mut self, addr:Word) -> Result<&mut ConnectedDevice, Exception>{
let off = addr - DEVICE_BASE_ADDR;
let idxb = self.devmap[(off / 2) as usize];
if idxb == 0{
throw!(ExceptionType::DeadDevice); // no device mapped here
}
return Ok(&mut self.devices[idxb as usize]);
}

这最终会调用getter

我不知道它是否适用于您的特定应用程序,但我觉得我应该指出,鉴于到目前为止共享的代码,可以使用一个特性。

pub trait Mapping {
const address: usize;

fn get(&mut self) -> Word;
fn set(&mut self, val: Word);
}
impl Mapping for SysDevices {
const address: usize = 0o177776;

fn get(&mut self) -> Word {
42
}

fn set(&mut self, val: Word) {
// Register magic
}
}

此外,您确定getter和setter实际上可以接受任何&mut dyn Device并且仍然正常工作吗?在我看来,它必须接受&mut Self

pub trait Device: Sized {
fn get_device_map(&self) -> Vec<DeviceMapping<Self>>;
fn reset(&mut self);
fn interrupt_ack(&mut self, slot: u32);
fn should_trace(&self) -> bool;
}
pub struct DeviceMapping<T> {
pub addr: Word,
// Functions could be dynamically sized closures that mutate their contents so I
// switched it to FnMut. If it truly is just a regular function, then a trait like
// above may be better suited to this.
pub setter: Box<dyn FnMut(&mut T, Word)>,
pub getter: Box<dyn FnMut(&mut T) -> Word>,
}
impl Device for SysDevices {
fn get_device_map(&self) -> Vec<DeviceMapping<Self>> {
let psw = DeviceMapping {
addr: 0o177776,
getter: Box::new(Self::get_psw),
setter: Box::new(Self::set_psw),
};
return vec![psw];
}
// etc...
}

解决此问题的最佳方法是使设备成为DeviceMapping结构和ConnectedDevice结构的类型参数:

pub struct DeviceMapping<T: Device> {
pub addr: Word,
pub setter: fn(&mut T, val: Word),
pub getter: fn(&mut T) -> Word
}
struct ConnectedDevice<T: Device> {
vector:Word,
device: T,
regmap:Vec<DeviceMapping<T>>
}

然后添加MappedDevice实现的一个新特性,该特性在设备类型上是通用的,以实现类型擦除:

trait MappedDevice {
fn get_word(&mut self, addr: Word) -> Word;
// Whatever the corrisponding set function is
fn set_word(&mut self, addr: Word) -> Word;
}
impl <T: Device> MappedDevice for ConnectedDevice<T> {
fn get_word(&mut self, addr: Word) -> Word {
let x = self.regmap.iter().find(|x| x.addr == addr).unwrap();
(x.getter)(&mut self.device)
}
// Corrisponding set function impl goes here.
}

然后,您可以从get_word函数中调用traits方法。

最新更新