4

I have a Component trait that has a method to return an index, like so:

trait Component {
    fn index(&self) -> usize;
}

These indexes are used for setting flags in a bitset. For example, a Component trait object returning an index of 5 would result in 5th bit being set in a container.

Currently I return a running index for each implementing type manually:

struct Foo;
struct Bar;

impl Component for Foo {
    fn index(&self) -> usize { 0 }
}

impl Component for Bar {
    fn index(&self) -> usize { 1 }
}

The trait objects are inserted into a container which keeps track of added components using a bitset:

struct Container<'a> {
    components: Vec<Component + 'a>,
    bits: BitSet
}

impl<'a> Container<'a> {
    fn add<T: Component + 'a>(&mut self, component: T) {
        self.components.push(component);
        self.bits.set(component.index());
    }
}

This works fine, but manually returning the index for each implementing type is cumbersome. How could I make it so that each implementing type would get its index automatically?

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
manabreak
  • 5,415
  • 7
  • 39
  • 96
  • I didn't understand anything to your question but take a look to [bitfield](https://github.com/dzamlo/rust-bitfield/) or [sparse_bitfield](https://docs.rs/sparse-bitfield/0.4.0/sparse_bitfield/) – Stargateur Jul 29 '18 at 05:48
  • @Stargateur I don't really understand how that relates to my question? I have my own, properly working bitset implementation already in place. – manabreak Jul 29 '18 at 05:50
  • Oh I was more thinking to [bitflags](https://github.com/bitflags/bitflags) sorry. But I still don't understand what you are trying to do, you are showing code that implement manually the value on two types, that really doesn't look like strong code for me, the only answer you will have currently is build a macro, your question doesn't include enough context to understand what you are doing. – Stargateur Jul 29 '18 at 05:56
  • @Stargateur I want to replace the manual value retrieval by macros or something else that generates the indexes automatically. – manabreak Jul 29 '18 at 08:07

1 Answers1

6

You can define a macro that calls itself recursively:

macro_rules! impl_component {
    // Empty case to end the recursion
    ($n:expr ;) => {};
    // Match the current count, the current type, and whatever else comes after
    ($n:expr ; $t:ty $(, $rest:tt)*) => {
        impl Component for $t {
            fn index(&self) -> usize { $n }
        }
        // Recurse, incrementing counter and only passing the remaining params
        impl_component!($n + 1; $($rest),*);
    };
    // For the first recursion, set the counter initial value to zero
    ($($types:tt),+) => { impl_component!(0; $($types),*); };
}

impl_component!(Foo, Bar, Baz);

The generated code will include implementations like this:

impl Component for Baz {
    fn index(&self) -> usize { 0 + 1 + 1 }
}

The compiler will collapse those expressions into literals, so the result is equivalent to what you wanted.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Peter Hall
  • 53,120
  • 14
  • 139
  • 204