this post was submitted on 10 Sep 2024
19 points (95.2% liked)

Rust

6029 readers
1 users here now

Welcome to the Rust community! This is a place to discuss about the Rust programming language.

Wormhole

[email protected]

Credits

  • The icon is a modified version of the official rust logo (changing the colors to a gradient and black background)

founded 1 year ago
MODERATORS
 

Hi all.

I want to develop a plugin system within my program, and I have a trait that functions defined by plugins should implement.

Currently, my code gets all the functions in a HashMap and then calls them by their name. Problem is, I have to create that hashmap myself by inserting every function myself.

I would really appreciate it if there was a way to say, suppose, all pub members of mod functions:: that implement this trait PluginFunction call register(hashmap) function. So as I add more functions as mod in functions it'll be automatically added on compile.

Pseudocode:

Files:

src/
├── attrs.rs
├── functions
│   ├── attrs.rs
│   ├── export.rs
│   └── render.rs
├── functions.rs
├── lib.rs

Basically, in mod functions I want:

impl AllFunctions{
    pub fn new() -> Self {
       let mut functions_map = HashMap::new();[[
       register_all!(crate::functions::* implementing PluginFunction, &mut functions_map);
       Self { function_map }
  }
}

Right now I'm doing:

impl AllFunctions{
    pub fn new() -> Self {
       let mut functions_map = HashMap::new();[[
       crate::functions::attrs::PrintAttr{}.register(&mut functions_map);
       crate::functions::export::ExportCSV{}.register(&mut functions_map);
       crate::functions::render::RenderText{}.register(&mut functions_map);
       // More as I add more functions
       Self { function_map }
  }
}
you are viewing a single comment's thread
view the rest of the comments
[–] [email protected] 8 points 2 months ago (1 children)

No, macros can see only the tokens you give them. They have no notion of the fact that crate::functions is a module, that PluginFunction is a trait and that functions_map is a variable. Not even the compiler may know those informations when the macro is expanded.

If you really really want to do this, you can use something like inventory. Note that inventory uses a special linker section to do this, which some consider a hack. This is also not supported on WASM if you want to target it.

[–] [email protected] 2 points 2 months ago (1 children)

Thank you. I just put the call with !, I don't necessarily want a macro solution. Any solution is acceptable, my requirement is that I can just keep adding more mods with functions in src/functions/ and not have to register each function.

Inventory seems like the solution I am looking for. Although in my case, instead of collecting different values of the same type, I want to collect different types that all have same trait. But maybe I can make a temporary struct with Box<dyn _> member to collect it if trying to collect it directly doesn't work. I do not plan to support WASM. I am planning to make C/C++ and Python API for the libraries though, so if it has problems with them, then I might have a problem.

[–] [email protected] 2 points 2 months ago (1 children)

Maybe keep maintaining the HashMap you have now and use one of these less portable mechanisms in a test to alert you when you forgot to register one?

[–] [email protected] 1 points 2 months ago

That seems like a good compromise if I don't find something better. Thank you.

I'm hoping to make it easy for people to add more functions, that's why I want minimal code change required to add more functions.