smoltcp-rs / smoltcp

a smol tcp/ip stack
BSD Zero Clause License
3.72k stars 412 forks source link

Possible to dynamic dispatch on `Device` trait? #957

Open Dr-TSNG opened 1 month ago

Dr-TSNG commented 1 month ago

I am trying to put different Device implementations into something like Box<dyn Device>. Moreover, I want to have something looks like

struct NetInterface {
    device: Box<dyn Device>,
    iface: Interface,
    sockets: SocketSet<'static>,
}

Without which, I have to write a lot of duplicate codes to support multiple physical network interfaces. However the trait doesn't meet object safe requirements. I wonder if this can be done with some tricks.

rvbcldud commented 1 month ago

Is doing something like the following forcing you to write a lot of duplicate code?

struct NetInterface<D> {
    device: D,
    iface: Interface,
    sockets: SocketSet<'static>,
}
mammothbane commented 2 weeks ago

I have a similar problem — I'm working on a virtual ethernet switch that operates over a variable number of unknown phy::Devices:

struct VSwitch<'a, const MAX_DEVICES: usize> {
    devices: heapless::Vec<MAX_DEVICES, &'a mut dyn smoltcp::phy::Device>,
}

This isn't possible with the current phy::Device trait as it's not object-safe.

I'm considering a wrapper/"trampoline" trait that impls phy::Device with TxToken and RxToken specialized to my crate, something like:

// These need to wrap the {Rx,Tx}Token traits, which I *think* (hope) can be done 
// with a closure -- might be impossible.
struct MyCrateTxToken;
struct MyCrateRxToken;

trait DeviceTrampoline {
    fn receive(&mut self, timestamp: Instant) -> Option<(MyCrateRxToken, MyCrateTxToken)>;
    fn transmit(&mut self, timestamp: Instant) -> Option<MyCrateTxToken>;
    fn capabilities(&self) -> DeviceCapabilities;
}

impl<T> DeviceTrampoline for T 
where T: phy::Device { 
    /* delegate */ 
}

// Now (I think) I can have:
struct VSwitch<'a, const MAX_DEVICES: usize> {
    devices: heapless::Vec<MAX_DEVICES, &'a mut dyn DeviceTrampoline>,
}

This feels unfortunate in that I think dynamic dispatch for phy::Device is generally useful, yet there isn't a canonical object-safe wrapper. That said, I'm not sure there's anything that can be done in smoltcp while maintaining the flexibility of TxToken and RxToken — I just wanted to provide another data point.