Hello, I'm trying to create a parameterized sync - async library to remove code duplication between synchronous and asynchronous versions, while allowing to swap modes at the individual call level. However, I have run into a roadblock
My design goal is to have user space code look like this:
use lib::{sync_async, Mode::{Sync, Async}};
#[sync_async]
fn a<M: Mode>() {
...
}
#[sync_async]
fn b<M: Mode>() {
a::<M>();
}
async fn main() {
b::<Sync>();
b::<Async>();
}
The #[sync_async] macro generates trait implementation that allows a() and b() to be called in both sync and async modes. I have already successfully implemented this step
However, the current issue is that the caller is still required to append .await. This causes the async and .await requirements to propagate up to the caller functions. I want the caller to simply type b::<Sync>() and b::<Async>(), and have the .await applied automatically when needed
To achieve this, my initial plan was to combine compile time reflection with compile time code generation that executes after the reflection step. Eg:
b::<Async>(); // Check if the generic parameter is `Sync` or `Async`. If `Sync`, generate `b();`. If `Async`, generate `b().await;`.
This would make #[sync_async] function calling another #[sync_async] function entirely portable since no .await is hardcoded. However, because macros expand before const code, this approach is impossible
Another approach I considered is having the #[sync_async] macro generate two distinct functions : name() and async name_async(). Then, a separate macro would determine which function to call based on the mode, eg: call!(Async, a())
The problem here is that macros can not handle propagated generic inputs from multiple callers. Eg:
#[sync_async]
fn a<M: Mode>() {
...
}
#[sync_async]
fn b<M: Mode>() {
call!(a::<M>());
}
async fn main() {
call!(b::<Sync>());
call!(b::<Async>());
}
Inside fn b(), the macro can not resolve what M is (Sync or Async) at the time of expansion to decide whether to generate a() or a().await
How to do this? Is there experimental feature to do code generation after const code? Or cost code that able to do code generation?