Description
Using frunk, and the following macro I managed to put together...
(You could think of it as a tuple, idexable by type)
macro_rules! create_pins {
($head:expr, $($tail:expr),+) => {
HCons<GpioPin<Unknown, $head>, create_pins!($($tail),*)>
};
($single:expr) => {
HCons<GpioPin<Unknown, $single>, HNil>
};
}
macro_rules! create_pins_head {
($head:expr, $tail:expr) => {
HCons<GpioPin<Unknown, $head>, $tail>
};
}
Using the macro like so:
type HetPinList = create_pins!(0, 1, 2, 3);
...expands to:
type HetPinList = HCons<GpioPin<Unknown, 0>,
HCons<GpioPin<Unknown, 1>,
HCons<GpioPin<Unknown, 2>,
HCons<GpioPin<Unknown, 3>,
HNil>>>>;
...which provides this handy interface
...so lets use it in our IO struct:
/// General Purpose Input/Output driver
pub struct IO<T: HList>
{
_io_mux: IO_MUX,
pins: T
}
impl<T: HList> IO<T> {
/// Construct the initial list of pins
pub fn new(gpio: GPIO, io_mux: IO_MUX) -> !/*IO<create_pins!(1, 2, 3)>*/ {
let pins = todo!(); // construct the actual pins
let io = IO {
_io_mux: io_mux,
pins,
};
io
}
}
impl<T: HList + Plucker<GpioPin<Unknown, { PIN_NO }>, HList>> IO<T> {
/// Claim a pin
pub fn pin<const PIN_NO: u8>(&mut self) -> GpioPin<Unknown, PIN_NO> {
let (pin, rest) = self.pins.pluck();
self.pins = rest;
pin
}
}
note: the pins field is now private, replaced by a special getter, and instead of let pin18 = io.gpio18
, you do let pin18 = io.pin::<18>()
... though this depends on IO::pin
taking &mut self
, which might be difficult.
another example: The ability to construct a USB like so, as the type system should be able to infer that the pins need to be pins 18, 19, and 20 (esp32s3):
let usb = USB::new(
peripherals.USB0,
io.pluck(),
io.pluck(),
io.pluck(),
&mut system.peripheral_clock_control,
);
That's unlikely, though, as the pin-list is a generic, changing it's monomorphised type each time you remove a pin
Possible pros that I see so far:
- The possibility of compile-time, integer indexing to select pins
- "auto-indexing" where the type system can infer which pin to take, such as when picking pins for
Usb::new
(each pin-arg can use only one pin) - It almost feels like GpioPins are a perfect use-case for Heterogeneous lists: Too different to be in an array without the overhead of dynamic dispatch (array of trait objects), and yet GpioPins are a series of the same thing, just with seperate, well defined, roles.
Possible cons:
- A very FP-based design. Those not familiar with FP style lists might have a tough time getting to grips with this
- Performance traps: Could this cause an explosion in the code size or compile time due to the huge(?i think?) leverage of compile-time generics
I'll continue investigating this...
Metadata
Metadata
Assignees
Type
Projects
Status