Шаблоны std::function со сложными типами

356
10 мая 2017, 09:51

Всем привет. Имеется программа моделирующая поведение системы n-тел во времени. Соотвественно у класса модель есть 2 типа функций - моделирующие движение по времени:

void explicit_euler(T period, T time_step, !тутдолжнабытьфункция);

и считающие вектор сил:

std::vector<vector_type> quadratic(const std::vector<object_type *>& objects);
std::vector<vector_type> finit_element(const std::vector<object_type *>& objects);

А также функция запускающая процесс с нужными параметрами:

void simulate (QString state_directory_name,
               T period,
               T time_step,
               time_method time = time_method::explicit_euler,
               force_method force = force_method::quadratic);

, где

enum class time_method { explicit_euler };
enum class force_method { quadratic, finit_element };
using system_type = gravity::system<T, dim>;
using object_type = gravity::object<T, dim>;
using vector_type = geometry::vector<T, dim>;

Как правильно формировать объект std::function в зависимости от выбранного метода расчёта сил?

Пока что-то такое. Но не рабочее, судя по всему.

template<typename T, size_t dim>
void model<T, dim>::simulate (QString state_directory_name,
                              T period, T time_step,
                              time_method time,
                              force_method force)
{
  std::function<std::vector<vector_type>(const std::vector<object_type *>&)> method;
  switch(force)
  {
    case force_method::quadratic:
    {
      method = quadratic;
      break;
    }
    case force_method::finit_element:
    {
      method = finit_element;
      break;
    }
  }
  switch(time)
  {
    case time_method::explicit_euler:
    {
      explicit_euler(period, time_step, method);
      break;
    }
  }
}
Answer 1

Для функций

std::vector<vector_type> quadratic(const std::vector<object_type *>& objects);
std::vector<vector_type> finit_element(const std::vector<object_type *>& objects);

правильный тип std::function будет

std::function<std::vector<vector_type>(const std::vector<object_type *>&)>

Такой пример компилируется:

#include<vector>
#include<array>
#include<functional>
using vector_type = std::array<int,3>;
using object_type = vector_type;
std::vector<vector_type> quadratic(const std::vector<object_type*>& objects){
    return {std::array<int,3>{1,2,3}};
}
std::vector<vector_type> finit_element(const std::vector<object_type*>& objects){
    return {};
}
volatile static int v = 0;
auto foo(){
    std::function<std::vector<vector_type>(const std::vector<object_type*>&)> fn;
    if(v == 0)
        fn = quadratic;
    else 
        fn = finit_element;
    return 0;
}

Есть подозрение, что quadratic и finit_element - это не свободные функции, а функции-члены (методы), указатель на которые - это не то, что вам нужно. Если это так, сделайте их статическими.

EDIT:

В c++ есть два типа функций: свободные функции и функции-члены. Функции-члены могут быть статическими и локальными. Статические функции эквивалентны свободным функциям, но находятся в области видимости класса, а не пространства имен.

Свободные и статические функции свободно преобразуются в указатель на функцию с соответствующей сигнатурой. Но нестатические функции-члены не могут быть напрямую преобразованы в указатель на функцию, т.к. они косвенно используют this, который им каким-то образом нужно передать. Тем не менее можно получить указатель на функцию-член, который абсолютно несовместим с обычными указателями на функцию и имеет специальный синтаксис.

std::function вносит синтаксический сахар и позволяет работать с функциями - членами так же, как и со свободными функциями, передавая первым параметром ссылку на объект класса. Реально это имеет смысл, если ваша функция использует не статические поля класса, в противном случае лучше объявить её как статическую.

Вот пример использования всех трех видов функций:

#include<vector>
#include<array>
#include<functional>
#include<iostream>
class MyInt{
   int v_;
public:
   MyInt(int v): v_{v} { }
   // Функция - член
   void setV(int v) { v_ = v; }
   int getV()const {return v_;}
   // Статическая функция
   static int static_setV(MyInt& self, int v) { 
      return self.v_ = v; // Обратите внимание, есть доступ к private - полю
      // Кроме того, для шаблонных типов доступны все соответствующие типы шаблона
   }
};
// Свободная функция
void free_setV(MyInt& i, int v){
  i.setV(v); // Нет доступа к private-полю, если не объявлена дружественной
}
int main (){
    std::function<void(MyInt&, int)> set_value_fn;
    MyInt i {0};
    std::cout << i.getV() <<std::endl;
    set_value_fn = MyInt::static_setV;
    set_value_fn(i, 1);
    std::cout << i.getV() <<std::endl;
    set_value_fn = free_setV;
    set_value_fn(i, 2);
    std::cout << i.getV() <<std::endl;
    set_value_fn = &MyInt::setV;
    set_value_fn(i, 3);
    std::cout << i.getV() <<std::endl;
    return 0;
}
Answer 2

Проблема решилась. Ошибка была в том что функции-члены класса и свободные функции оборачиваются в std::function по разному. Тщательно проверив код и сравнив типы операндов я аккуратно написал обертку.

Функция запуска процесса по параметрам:

template<typename T, size_t dim>
void model<T, dim>::simulate (QString state_directory_name,
                              T period, T time_step,
                              time_method time,
                              force_method force)
{
  std::function<std::vector<vector_type>(const model<T, dim>&, const std::vector<object_type *>&)> method;
  switch(force)
  {
    case force_method::quadratic:
    {
      method = &model<T,dim>::quadratic;
      break;
    }
    case force_method::finit_element:
    {
      method = &model<T,dim>::finit_element;
      break;
    }
  }
  switch(time)
  {
    case time_method::explicit_euler:
    {
      explicit_euler(period, time_step, method);
      break;
    }
  }
}

Замечу, что я дополнительно указал ключевое слово const, поскольку эти функции не будут менять члены класса:

std::vector<vector_type> quadratic(const std::vector<object_type *>& objects) const;
std::vector<vector_type> finit_element(const std::vector<object_type *>& objects) const;

Если они будут менять члены класса - const надо будет убрать и у const model<T, dim>&.

Прототип функции в которую я передаю сформированный объект std::function теперь выглядит так:

void explicit_euler(T period, T time_step,
                    std::function<std::vector<vector_type>(const model<T, dim>&, const std::vector<object_type *>&)> method);

При инстанцировании std::function надо не забывать, что если это обёртка для шаблонной функции - параметры шаблонов должны передаваться обязательно: Не const model&, а const model<T, dim>&, к примеру.

Также, в вызов обёрнутой функции нужно добавлять ссылку на объект, у которого функция вызывается. В моем случае так (поскольку я вызываю функцию объекта того же класса, в котором и нахожусь).

method(*this, objects);
READ ALSO
Функция MPI_Bcast не передает массив

Функция MPI_Bcast не передает массив

Изучаю параллельное программирование (MPI)При попытке передать массив всем процессам с помощью функции MPI_Bcast, сталкиваюсь с тем, что его получает...

273
Объявление классов внутри функций

Объявление классов внутри функций

Можно ли объявлять классы внутри функций? А передавать созданные таким образом объекты в другие функции? Например, данный код выполняется,...

242
Версия BIOS с помощью С++

Версия BIOS с помощью С++

Добрый день! Никак не удаётся написать или найти программу, которая позволяла бы выводить на экран версию BIOS текущего компьютераНужен код...

292
Запись в бинарный файл структуры

Запись в бинарный файл структуры

Доброго дняПо заданию требуется записать в бинарник несколько массивов структур

325