使用lambda修改由packed参数标识的引用?

问题描述 投票:3回答:2

我正在尝试在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;
});

但是,即使打包参数...CVelocity, Position)匹配lambda(Velocity, Position)中的参数,这也会产生“无匹配错误”。我无法解决这个问题。

然而,主要的问题是,如果lambda参数可以简化为像单个mod函数,如this([](auto& vel, auto& pos),也可以作为参考转发。我觉得这应该是可能的,因为编译器不知道什么,但我的C ++是有限的。

c++ lambda c++17 variadic-templates generic-lambda
2个回答
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参数类型来删除引用和const-ness。

lambdas的另一个巧妙的技巧是将它们转换为函数指针(我在第一个例子中展示过)。非捕获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;
}

如果你不需要捕获lambdas并且可以容忍一元+那么你可以使用mod_comp的函数指针重载但是为此,你可能想要捕获lambdas。


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

这应该只是解决你的主要问题,允许你传递一个lambda(或一个不同的callable),其签名不完全对应C...,所以也有一些auto &参数。

© www.soinside.com 2019 - 2024. All rights reserved.