Last active
August 29, 2015 14:22
-
-
Save krdln/57ebc1f1940cdd434646 to your computer and use it in GitHub Desktop.
ECS with tuple trait magic
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::collections::BTreeMap; | |
extern crate typemap; | |
use typemap::{TypeMap, Key}; | |
/// Keys for my ECS | |
trait MyKey: Key where <Self as Key>::Value: Clone {} | |
struct Ecs { | |
// just an example layout. totally not cache-friendly | |
entities: BTreeMap<usize, TypeMap> | |
} | |
/// Trait describing types, which Ecs::collect can be parametrized over | |
trait CollectArg { | |
type Ret; | |
fn filter(tm: &TypeMap) -> Option<Self::Ret>; | |
} | |
// impl<T: Key> would collide with impl for tuples | |
impl<T: MyKey> CollectArg for T | |
where <T as Key>::Value: Clone // (why do I have to repeat this bound here?) | |
{ | |
type Ret = T::Value; | |
fn filter(tm: &TypeMap) -> Option<Self::Ret> { | |
tm.get::<T>().cloned() | |
} | |
} | |
macro_rules! otry{ ($e:expr) => ( if let Some(x) = $e { x } else { return None } ) } | |
impl<T: CollectArg, U: CollectArg> CollectArg for (T, U) { | |
type Ret = (T::Ret, U::Ret); | |
fn filter(tm: &TypeMap) -> Option<Self::Ret> { | |
Some( ( otry!(T::filter(tm)), otry!(U::filter(tm)) ) ) | |
} | |
} | |
impl<T: CollectArg, U: CollectArg, V: CollectArg> CollectArg for (T, U, V) { | |
type Ret = (T::Ret, U::Ret, V::Ret); | |
fn filter(tm: &TypeMap) -> Option<Self::Ret> { | |
Some( ( otry!(T::filter(tm)), otry!(U::filter(tm)), otry!(V::filter(tm)) ) ) | |
} | |
} | |
// you can add impls for longer tuples, or just nest them | |
// this is just addition i've added for fun | |
struct Exclude<T>(T); | |
impl<T: MyKey> CollectArg for Exclude<T> { | |
type Ret = (); | |
fn filter(tm: &TypeMap) -> Option<()> { | |
if let Some(_) = tm.get::<T>() { None } | |
else { Some(()) } | |
} | |
} | |
impl Ecs { | |
fn new() -> Ecs { Ecs { entities: BTreeMap::new() } } | |
fn set<T: MyKey>(&mut self, id: usize, val: T::Value) { | |
self.entities.entry(id).or_insert_with(||TypeMap::new()).insert::<T>(val); | |
} | |
fn collect<What: CollectArg>(&self) -> Vec<(usize, What::Ret)> { | |
self.entities.iter().filter_map( |(&i,tm)| What::filter(tm).map(|x|(i,x)) ).collect() | |
} | |
} | |
// 8< -------------------------------------------------------------------------------------- >8 | |
// example: | |
struct Position; | |
impl Key for Position { type Value = P; } | |
impl MyKey for Position {} | |
struct Velocity; | |
impl Key for Velocity { type Value = P; } | |
impl MyKey for Velocity {} | |
struct Name; | |
impl Key for Name { type Value = &'static str; } | |
impl MyKey for Name {} | |
#[derive(Debug, Clone)] | |
struct P(i32, i32); | |
fn main() { | |
let mut e = Ecs::new(); | |
e.set::<Name>(0, "background"); | |
e.set::<Name>(1, "car"); | |
e.set::<Position>(1, P(25, 2)); | |
e.set::<Velocity>(1, P(10, 0)); | |
e.set::<Name>(2, "raindrop"); | |
e.set::<Position>(2, P(35, 10)); | |
e.set::<Velocity>(2, P(0, -5)); | |
e.set::<Name>(3, "house"); | |
e.set::<Position>(2, P(0, 0)); | |
println!("\none argument"); | |
for (i, name) in e.collect::<Name>() { | |
println!(" {}: {}", i, name); | |
} | |
println!("\nmany arguments"); | |
for (i, (name, x, v)) in e.collect::<(Name, Position, Velocity)>() { | |
println!(" {}: {} {:?} {:?}", i, name, x, v); | |
} | |
println!("\ntuple nesting"); | |
for (i, (name, (x, v))) in e.collect::<(Name, (Position, Velocity))>() { | |
println!(" {}: {} {:?} {:?}", i, name, x, v); | |
} | |
println!("\nmore magic"); | |
for (i, (name, _)) in e.collect::<(Name, Exclude<Velocity>)>() { | |
println!(" {}: {} can't move", i, name); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment