3

我正在尝试在我的 WIP 小型“游戏框架”中创建一个很好的实体组件修改功能。但是,当我尝试修改多个组件(使用打包参数)时,我一直在创建函数

这是我对单个组件的功能,它可以正常工作并且表现得像我喜欢的那样

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;
});

但这会产生“不匹配错误”,即使打包参数...C( Velocity, Position) 与 lambda ( Velocity, Position) 中的参数匹配。我想不出解决这个问题。

然而,主要问题是 lambda 参数是否可以像这样的单个 mod 函数那样简化,([](auto& vel, auto& pos)并且也可以作为引用转发。我觉得这应该是可能的,因为编译器没有什么是未知的,但我的 C++ 是有限的。

4

2 回答 2

4

抱歉,但是...考虑到您显C...式调用可变参数类型列表mod_comp()...如果将 lambda 简单地作为可执行文件传递,而忽略与C...列表的连接呢?

我的意思是......下面的东西呢(注意:代码未经测试)

template <typename ... C, typename F>
void mod_comp (Entity ent, F && f)
 { return std::forward<F>(f)(get_comp<C>(ent)...); }

C...这也应该简单地解决您的主要问题,允许您传递一个 lambda(或不同的可调用),其签名与某些auto &参数不完全对应。

于 2019-04-19T08:10:09.927 回答
2

您可以做一些偷偷摸摸的技巧来获取 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。

于 2019-04-19T04:25:37.100 回答