Macro to ease factory design pattern

What I want to solve is

lookup!(cfg, val => [A, B, C, D, ... Z]);

So example:

let c: C = lookup!("blarf", 2 => [A, B, C, D, ...Z]) // Equivalent of calling C::create("blarf");

All types implement trait

trait Create {
    create(cfg: ...) -> Self;
}

The macro definition would look something like:

macro_rules! lookup {
    ($cfg:expr, $m:expr => [ $( $a:ty ),* ]) => {
        match $m {
            $(
                i => <$a>::create(cfg)
            )*
        }
    }
}

But I cannot figure out how the macro would be for the match statement. Any suggestions?

P.S.
I could of course just write a function as:

let i = 2;
let cfg = ...
let c: C = match i {
    0 => A::create(cfg);
    1 => B::create(cfg);
    2 => C::create(cfg);
...
};

Every arm of a match has to evaluate to the same type. Types can't be dependent on runtime values, modulo being erased (e.g dyn Trait, *const ()).

2 Likes

If you know the types ahead of time, you can move the comparison into the macro system

#![allow(dead_code, unused_variables)]

trait Create {
    fn create(cfg: &'static str) -> Self;
}

struct C;
impl Create for C {
    fn create(cfg: &'static str) -> Self {
        todo!()
    }
}

macro_rules! lookup {
    ($cfg:expr, $m:tt) => {
        <lookup!(@internal $m)>::create($cfg)
    };
    (@internal 0) => {
        A
    };
    (@internal 1) => {
        B
    };
    (@internal 2) => {
        C
    }
}

fn check() {
    let c: C = lookup!("blarf", 2);
}

I'm not sure if that will be helpful to what you're trying to do though

Thanks, this achieves what I want!