-
Notifications
You must be signed in to change notification settings - Fork 3
Add optional async_suffix attribute to bisync macro #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
I'm not sure if this is a good addition to the library, so I have a couple of questions. Is this useful for anything else than wrapping a pair of functions to have the same name in the current scope? I imagine it gets weird once you have multiple calls in a function body, some of which might need the suffix and some of which might not need it. Would it not be possible to achieve the same using something like this (roughly the same amount of code): #[only_sync]
use path::to::Struct::read as Struct_read;
#[only_async]
use path::to::Struct::read_async as Struct_read;You could even put all of these #[path = "."]
pub mod asynchronous {
use bisync::asynchronous::*;
use path::to::somewhere::read as read;
mod inner;
pub use inner::*;
}
#[path = "."]
pub mod blocking {
use bisync::synchronous::*;
use path::to::somewhere::read_async as read;
mod inner;
pub use inner::*;
} |
|
Well, the primary goal is exactly that, not just a pair, all pairs of functions with symmetric names. It's true, it does get weird if I'd have multible calls in function body, but for this use case it works, all the async calls would be from the same device-driver crate, which all have _async suffix. #[only_sync]
use path::to::Struct::read as Struct_read;
#[only_async]
use path::to::Struct::read_async as Struct_read;is not really possible in my case, as I'm calling the methods on generated code. Higher in my code I have this line: device_driver::create_device!(device_name: AxpLowLevel, manifest: "device.yaml");it generated code from a yaml with registers description. The yaml has 2040 lines of code, which generate 18K lines of Rust code with the macro. Total number of fields in registers is about 160 and almost all of them have read / read_async, write / write_async, modify / modify_async. so the first problem is: there are too many fields with these methods to write so I tried doing my best to prevent code dublication |
|
@JM4ier here's how I use it https://github.com/okhsunrog/axp192-dd |
|
Any suggestions how to improve it? |
|
another suggestion: #[bisync]
async fn read_status_register(&mut self) -> Result<StatusFieldSet, MyError> {
// Automatically calls .read_async().await in async, .read()? in sync
let status_data = bisync_suffix!("_async", self.low_level_device.status_reg().read().await?);
// Don't change suffix here
Timer::after(Duration::from_secs(1)).await;
Ok(status_data)
} |
|
I already have a draft of this approach that works for me, I can crete a second PR, if you like this approach more |
|
I released |
|
I only got time to look at it now, sorry for the delay. The main objective of me writing another library to write async-generic code is to be more minimal than maybe-async, i.e. to not need to depend on syn parsing and quoting, and to work in the presence of feature unification.
Another thing I'd like to note is that |
|
I don't think that enabling both blocking and async support of the driver makes sense. A system that has the axp192 power management IC almost always has only one such IC, and the interface that get's passes to Axp192 when creating one usually implements either I2C from |
|
I can do it another way probably, introducing a single feature flag |
For a direct user of the library it might not make a lot of sense but it does have effects on downstream libraries that might use
the #[path = "."]
mod asynchronous {
use bisync::asynchronous::*;
use embedded_hal_async::spi::SpiBus;
use smart_leds_trait::SmartLedsWriteAsync as SmartLedsWrite;
mod writer;
pub use writer::*;
}
pub use asynchronous::Apa102Writer as Apa102WriterAsync;
#[path = "."]
mod blocking {
use bisync::synchronous::*;
use embedded_hal::spi::SpiBus;
use smart_leds_trait::SmartLedsWrite;
#[allow(clippy::duplicate_mod)]
mod writer;
pub use writer::*;
}
pub use blocking::Apa102Writer;note the duplicate |
|
@JM4ier thank you, I put some time and impoved my |
This PR makes
#[bisync]a bit smarter for async code. You can now tell it to automatically add a suffix to your.awaited method calls.For example, if your underlying library has
device.read()anddevice.read_async(), you can now do this:This helps avoid manual
#cfgs for these common async/sync method differences.How it Works:
asynchronousmodule:#[bisync(async_suffix = "your_suffix")]will changemethod().awaittomethod_your_suffix().await.#[bisync](no suffix argument) on anasync fnwill still work as before – no magic renaming of.awaitcalls inside.#[bisync]is unchanged.Why is this useful? (Example with
device-driver)The
device-drivercrate generates register access methods likesome_register().read()for synchronous operations andsome_register().read_async()for asynchronous ones.Without this PR, you might write:
With this PR, it becomes cleaner:
This significantly reduces boilerplate when wrapping
device-drivergenerated APIs or similar libraries with distinct sync/async method names.