您可以做一些偷偷摸摸的技巧来获取 lambda 的签名并使用它来获取组件。
template <typename Class, typename... Params>
void mod_comp_helper(Entity ent, Class *obj, void (Class::*fun)(Params...) const) {
(obj->*fun)(get_comp<std::decay_t<Params>>(ent)...);
}
// optional overload for mutable lambdas
template <typename Class, typename... Params>
void mod_comp_helper(Entity ent, Class *obj, void (Class::*fun)(Params...)) {
(obj->*fun)(get_comp<std::decay_t<Params>>(ent)...);
}
template <typename Functor>
void mod_comp(Entity ent, Functor &&fun) {
mod_comp_helper(ent, &fun, &std::decay_t<Functor>::operator());
}
// optional overload for function pointers
template <typename... Params>
void mod_comp(Entity ent, void(*fun)(Params...)) {
fun(get_comp<std::decay_t<Params>(ent)>...);
}
int main() {
mod_comp(ent, [](Velocity &vel, Position &pos) {
// modify components
});
// you can use std::function if you want
// although you probably don't need to
std::function<void(Velocity &, Position &)> fun = [](Velocity &vel, Position &pos) {
// modify components
};
mod_comp(ent, fun);
// this calls the function pointer overload
mod_comp(ent, +[](Velocity &vel, Position &pos) {
// modify components
});
}
lambda 表达式实际上只是构造函子(带有调用运算符的对象)的语法糖。
struct __anonymous_compiler_generated_class__ {
void operator()(int i) const {
// ...
}
};
int main() {
auto lambda = [](int i) {
// ...
};
// above is sugar for this:
auto functor = __anonymous_compiler_generated_class__{};
}
一个 lambda 表达式构造一个闭包对象。这个对象有一个operator()
. 我们可以获取呼叫接线员的地址并推断其签名。然后我们只是std::decay_t
删除引用和常量的参数类型。
lambda 的另一个巧妙技巧是将它们转换为函数指针(我在第一个示例中展示过)。非捕获 lambda 可以隐式转换为函数指针。您可以使用一元+
或 astatic_cast
来强制进行此转换。这里还有几个例子:
int main() {
auto lambda = [](int i) {};
void (*fnptr0)(int) = lambda;
auto fnptr1 = +lambda;
auto fnptr2 = static_cast<void(*)(int)>(lambda);
int capture;
auto capturing_lambda = [capture](int i) {};
// compiler says no
// auto fnptr3 = +capturing_lambda;
}
如果您不需要捕获 lambda 并且可以容忍一元,+
那么您可以只使用函数指针重载,mod_comp
但为此,您可能想要捕获 lambda。