Hi, I want to work at some code and I want to simplify it because it is really verbose and repetitive.
Can this be simplified and more generic?
Hi, I want to work at some code and I want to simplify it because it is really verbose and repetitive.
Can this be simplified and more generic?
I would use a macro. Declarative macros are perfect for places where you would otherwise to copy/paste a chunk of code multiple times and only tweak one or two names.
Thanks, but I have never programmed a macro before, so I have no clue where to start. Can you give me a hint?
Tried something like this.
#[macro_export]
macro_rules! avr {
( $( $x:expr ),* ) => {
#[cfg(feature = "$x")]
pub mod $x;
#[cfg(feature = "$x")]
impl $x::Peripherals {
/// Returns all the peripherals *once*
#[inline]
pub fn take() -> Option<Self> {
crate::interrupt::free(|_| {
if unsafe { DEVICE_PERIPHERALS } {
None
} else {
Some(unsafe { $x::Peripherals::steal() })
}
})
}
}
};
}
But is this correct? And how do I use it?
Try something like
macro_rules! peripherals {
($(
$(#[$doc_comments:meta])*
$name:ident $feature:literal
)*) => {
$(
$(#[$doc_comments])*
#[cfg(feature = $feature)]
pub mod $name;
#[cfg(feature = $feature)]
impl $name::Peripherals {
/// Returns all the peripherals *once*
#[inline]
pub fn take() -> Option<Self> {
crate::interrupt::free(|_| {
if unsafe { DEVICE_PERIPHERALS } {
None
} else {
Some(unsafe { $name::Peripherals::steal() })
}
})
}
}
)*
}
}
peripherals! {
/// [AT90USB1286](https://www.microchip.com/wwwproducts/en/AT90USB1286)
at90usb1286 "at90usb1286"
/// [ATmega1280](https://www.microchip.com/wwwproducts/en/ATmega1280)
atmega1280 "atmega1280"
// etc...
}
Or with the paste crate, this could be simplified [1] to something like
macro_rules! peripherals {
($(
$name:ident
)*) => {
paste::paste!{$(
#[doc = " [" $name "](https://www.microchip.com/wwwproducts/en/" $name ")"]
#[cfg(feature = $name:lower)]
pub mod [<$name:lower>];
#[cfg(feature = $name:lower)]
impl [<$name:lower>]::Peripherals {
/// Returns all the peripherals *once*
#[inline]
pub fn take() -> Option<Self> {
crate::interrupt::free(|_| {
if unsafe { DEVICE_PERIPHERALS } {
None
} else {
Some(unsafe { [<$name:lower>]::Peripherals::steal() })
}
})
}
}
)*}
}
}
peripherals! {
AT90USB1286
ATmega1280
// etc...
}
well… the macro definition becomes more complicated, but the macro call in the end is what’s simplified ↩︎
Thank you very much. I will try this as soon as possible and give you feedback. Than I will try to understand the code and how it works because currently I have no clue.
If you are feeling uncertain about how macros work, check out the Macros chapter from The Book for a basic introduction. After you feel like you grasp the basics, check out The Little Book of Rust Macros for some more advanced techniques and applications.
@MarkuBu also beyond the introduction in the book, for a more complete overview of everything that macro_rules
macros technically support / can offer, see Macros By Example - The Rust Reference
Particularly useful: the list of “fragment specifiers”.
For the example involving paste, of course the documentation of the paste
crate is useful.
Another important aspect to understand the macros I wrote: Doc comments
/// Some contents
/// here, documenting stuff!
are equivalent to (and desugared very early in compilation into) a sequence of doc
attributes
#[doc = " Some contents"]
#[doc = " here, documenting stuff!"]
And for the paste
code, it’s important to note this section, and apparently (maybe undocumented) the paste
macro handles arguments to attributes other than doc =
similarly, e.g. used for the #[cfg(feature = …)]
attributes.