Использовать лямбду для изменения ссылок, идентифицированных упакованным параметром? - PullRequest
3 голосов
/ 19 апреля 2019

Я пытаюсь создать красивую функцию модификации компонентов сущности в моей небольшой игровой структуре 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) соответствует параметрам в лямбде (Velocity, Position).Я не могу понять, как это исправить.

Основная проблема, однако, заключается в том, что параметры лямбды можно упростить, например, функцию одиночного мода, например, ([](auto& vel, auto& pos), а также пересылать в качестве ссылок.,Я чувствую, что это должно быть возможно, так как компилятору ничего не известно, но мой C ++ ограничен.

Ответы [ 2 ]

4 голосов
/ 19 апреля 2019

Извините, но ... принимая во внимание, что вы явно указали переменный список типов C..., вызывающий mod_comp() ... как насчет передачи лямбды просто как исполняемого файла, игнорируя соединение со списком C...?

Я имею в виду ... что-то вроде следующего (осторожно: код не проверен)

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

Это должно просто решить и вашу основную проблему, позволяя вам передавать лямбду (или другой вызываемый объект) с сигнатурой, точно не соответствующей C..., а также с некоторыми auto & параметрами.

2 голосов
/ 19 апреля 2019

Вы можете сделать несколько хитрых трюков, чтобы получить сигнатуру лямбды и использовать ее для получения компонентов.

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

Лямбда-выражение на самом деле является просто синтаксическим сахаром для создания функтора (объекта соператор вызова).

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__{};
}

Лямбда-выражение создает объект замыкания.Этот объект имеет operator().Мы можем взять адрес оператора звонка и вывести его подпись.Затем мы просто std::decay_t типы параметров для удаления ссылок и константности.

Еще один хитрый трюк с лямбдами - это преобразование их в указатели на функции (что я показал в первом примере).Неполные лямбды могут быть неявно преобразованы в указатели функций.Вы можете использовать унарный + или 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;
}

Если вам не требуется захват лямбд и вы можете допустить унарный +, тогда вы можете просто использовать перегрузку указателя функции mod_comp, но для этогоВы, вероятно, хотите захватить лямбды.

...