Reputation: 760
I'm trying to learn about macros, as meta programming is a concept I really loved when I found about it, and it can be really usefull in my current project.
I'm trying to make a macro to create entities with any number of components, something like :
let ecs = ECS::new();
let entity1 = create_entity!(ecs);
let entity2 = create_entity!(ecs, Position{x:1, y:2});
let entity3 = create_entity!(ecs, Position{x:3, y:-1}, Velocity{vx:0, vy:1});
There are several things I don't know how to do here :
println!()
will allways have a string first ? I've looked the definition of println!()
, but couldn't look deeper into the functions that are called.As an example, here is a working function to create an entity with one component :
fn create_entity_1<C1, C2>(ecs: ECS, component1: C1, component2: C2) -> Entity {
let result_entity = ecs.create_entity();
ecs.components.add_comp_to_last(&result_entity, component1);
ecs.components.add_comp_to_last(&result_entity, component2);
return result_entity;
}
And obviously, with more component I "simply" need to add more of the second line.
This is what i've tried so far, but I'm struggling to understand what I'm doing :
// macros to create entities with any number of components
macro_rules! create_entity {
($ecs:expr; $($comp:literal),*) => {
let result_entity = $ecs.create_entity();
$ecs.components.add_comp_to_last(&result_entity, comp);
}
}
I've tried to read both The book and The little book of macros, But I'm lacking concrete example or explanations of the whole thing.
Upvotes: 1
Views: 140
Reputation: 59817
You look like you were on the right track so let me fill in the rest:
macro_rules! create_entity {
($ecs:expr) => { ECS::create_entity(&$ecs) };
($ecs:expr; $($comp:expr),*) => { {
let result_entity = ECS::create_entity(&$ecs);
$(
$ecs.components.add_comp_to_last(&result_entity, $comp);
)*
result_entity
} };
}
$ecs:expr
and $ecs:expr; $($comp:expr),*
into separate rules, the former of which simply creates the entity without components.$comp
from literal
to expr
. Only things like "strings"
, 42
, or true
are literals and you likely want to be able to accept any expression that creates a component anyway.{ ...; result_entity }
(note the extra braces above { { ... } }
). That way you can assign the result of the macro to a variable as you desired, otherwise let entity = create_entity!(ecs, ...);
wouldn't work since the macro would yield multiple statements instead of a single block of statements.$comp
to multiple expressions using $(...)*
within the macro output.ECS::create_entity()
directly if you want to ensure that the $ecs
is an ECS
(or reference to one) and not simply accept variables that could act like an ECS
. The println!
macro works differently since a macro can distinguish between kinds of literals (first parameter can't just be an expression of type &str
it must be a literal), but here you can't distinguish in a macro between a variable of type ECS
and a variable of some other type. You simply have to construct the generated code to produce an error if it wasn't the expected type.See it working on the playground.
You use a comma separator after ecs
in your example usage but you use a semicolon when defining the macro rule (meaning create_entity!(ecs; ...)
), I kept with the semicolon here but its trivial to change.
Upvotes: 4