使用 lambda 修改由打包参数标识的引用?
Use lambda to modify references identified by a packed parameter?
我正在尝试在我的 WIP 小型 "game framework" 中创建一个很好的 modify 实体组件的功能。但是,当我尝试 mod 验证多个组件(使用打包参数)
时,我一直坚持创建函数
这是我针对单个组件的函数,它工作正常并且表现如我所愿
template <typename C>
void mod_comp(Entity ent, std::function<void(C&)> cb) {
auto& c = get_comp<C>(ent);
return cb(c);
}
// Called like this
mng.mod_comp<Velocity>(ent, [](auto& vel) {
// Modify velocity force value
vel.f += 5;
});
在尝试为多个组件创建函数时,我发现了 2 个我不知道如何解决的问题。
template <typename... C>
void mod_comp(Entity ent, std::function<void(C...)> cb) {
return cb(get_comp<C>(ent)...);
}
// Supposed to be called like this
mng.mod_comp<Velocity, Position>(ent, [](Velocity vel, Position pos) {
pos.x += vel.f;
});
但这给出了 "no match error" 即使压缩参数 ...C
(Velocity, Position
) 与 lambda (Velocity, Position
) 中的参数相匹配。我想不出解决这个问题的方法。
但是主要问题 是 lambda 参数是否可以像单个 mod 函数那样简化 ([](auto& vel, auto& pos)
并作为参考转发。我觉得这应该是可能的,因为编译器没有什么是未知的,但我的 C++ 是有限的。
您可以采取一些偷偷摸摸的技巧来获取 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 可以隐式转换为函数指针。您可以使用一元 +
或 static_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。
抱歉,但是...考虑到您显式调用 mod_comp()
的可变参数 C...
类型列表...如何将 lambda 简单地作为可执行文件传递,忽略与 C...
列表?
我的意思是...下面的内容怎么样(注意:代码未经测试)
template <typename ... C, typename F>
void mod_comp (Entity ent, F && f)
{ return std::forward<F>(f)(get_comp<C>(ent)...); }
这也应该简单地解决你的主要问题,允许你传递一个 lambda(或不同的可调用),其签名不完全对应于 C...
所以也有一些 auto &
参数。
我正在尝试在我的 WIP 小型 "game framework" 中创建一个很好的 modify 实体组件的功能。但是,当我尝试 mod 验证多个组件(使用打包参数)
时,我一直坚持创建函数这是我针对单个组件的函数,它工作正常并且表现如我所愿
template <typename C>
void mod_comp(Entity ent, std::function<void(C&)> cb) {
auto& c = get_comp<C>(ent);
return cb(c);
}
// Called like this
mng.mod_comp<Velocity>(ent, [](auto& vel) {
// Modify velocity force value
vel.f += 5;
});
在尝试为多个组件创建函数时,我发现了 2 个我不知道如何解决的问题。
template <typename... C>
void mod_comp(Entity ent, std::function<void(C...)> cb) {
return cb(get_comp<C>(ent)...);
}
// Supposed to be called like this
mng.mod_comp<Velocity, Position>(ent, [](Velocity vel, Position pos) {
pos.x += vel.f;
});
但这给出了 "no match error" 即使压缩参数 ...C
(Velocity, Position
) 与 lambda (Velocity, Position
) 中的参数相匹配。我想不出解决这个问题的方法。
但是主要问题 是 lambda 参数是否可以像单个 mod 函数那样简化 ([](auto& vel, auto& pos)
并作为参考转发。我觉得这应该是可能的,因为编译器没有什么是未知的,但我的 C++ 是有限的。
您可以采取一些偷偷摸摸的技巧来获取 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 可以隐式转换为函数指针。您可以使用一元 +
或 static_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。
抱歉,但是...考虑到您显式调用 mod_comp()
的可变参数 C...
类型列表...如何将 lambda 简单地作为可执行文件传递,忽略与 C...
列表?
我的意思是...下面的内容怎么样(注意:代码未经测试)
template <typename ... C, typename F>
void mod_comp (Entity ent, F && f)
{ return std::forward<F>(f)(get_comp<C>(ent)...); }
这也应该简单地解决你的主要问题,允许你传递一个 lambda(或不同的可调用),其签名不完全对应于 C...
所以也有一些 auto &
参数。