Указатель на функцию-член и не-член

Абстрактный

У меня есть класс, который хранит проблему оптимизации и запускает решатель этой проблемы. Если решатель не работает, я хочу рассмотреть подзадачу и решить ее, используя тот же решатель (и класс).

Введение

Задача оптимизации состоит из множества (математических) функций. Функции задачи определены вне класса, но функции подзадач определены внутри класса, поэтому они имеют разные типы (например, void (*) и void (MyClass::*).

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

Пример кода

Пример кода для имитации моей проблемы:

#include <iostream>

using namespace std;

typedef void (*ftype) (int, double);

// Suppose foo is from another file. Can't change the definition
void foo (int n, double x) {
  cout << "foo: " << n*x << endl;
}

class TheClass {
  private:
    double value;
    ftype m_function;
    void print (int n, double x) {
      m_function(size*n, value*x);
        }
  public:
    static int size;
    TheClass () : value(1.2), m_function(0) { size++; }
    void set_function (ftype p) { m_function = p; }
    void call_function() {
      if (m_function) m_function(size, value);
    }
    void call_ok_function() {
      TheClass ok_class;
      ok_class.set_function(foo);
      ok_class.call_function();
    }
    void call_nasty_function() {
      TheClass nasty_class;
//      nasty_class.set_function(print);
//      nasty_class.set_function(&TheClass::print);
      nasty_class.call_function();
    }
};
int TheClass::size = 0;

int main () {
  TheClass one_class;

  one_class.set_function(foo);
  one_class.call_function();
  one_class.call_ok_function();
  one_class.call_nasty_function();
}

Как следует из примера, функция-член не может быть статической. Кроме того, я не могу переопределить исходную проблемную функцию для получения объекта.

Спасибо за любую помощь.

Редактировать

Я забыл упомянуть. Я попытался перейти на std::function, но моя исходная функция имеет более 10 аргументов (это подпрограмма Fortran).

Решение

Я внес изменения в std::function и std::bind, как было предложено, но не стал переделывать функцию с более чем 10 аргументами. Я решил создать промежуточную функцию. Следующий код иллюстрирует то, что я сделал, но с меньшим количеством переменных. Спасибо всем.

#include <iostream>
#include <boost/tr1/functional.hpp>

using namespace std;

class TheClass;

typedef tr1::function<void(int *, double *, double *, double *)> ftype;

// Suppose foo is from another file. Can't change the definition
void foo (int n, int m, double *A, double *x, double *b) {
    // Performs matrix vector multiplication x = A*b, where
    // A is   m x n
}

void foo_wrapper (int DIM[], double *A, double *x, double *b) {
    foo(DIM[0], DIM[1], A, x, b);
}

class TheClass {
    private:
        ftype m_function;
        void my_function (int DIM[], double *A, double *x, double *b) {
            // Change something before performing MV mult.
            m_function(DIM, A, x, b);
        }
    public:
        void set_function (ftype p) { m_function = p; }
        void call_function() {
            int DIM[2] = {2,2};
            if (m_function) m_function(DIM, 0, 0, 0);
        }
        void call_nasty_function() {
            TheClass nasty_class;
            ftype f = tr1::bind(&TheClass::my_function, this, _1, _2, _3, _4);
            nasty_class.set_function(f);
            nasty_class.call_function();
        }
};

int main () {
    TheClass one_class;

    one_class.set_function(foo_wrapper);
    one_class.call_function();
    one_class.call_nasty_function();
}

PS. Создание std::function с более чем 10 переменными казалось возможным (скомпилировано, но я не тестировал) с помощью

#define BOOST_FUNCTION_NUM_ARGS 15
#include <boost/function/detail/maybe_include.hpp>
#undef BOOST_FUNCTION_NUM_ARGS

Но создать std::bind для более чем 10 аргументов не так просто.


person Abel Siqueira    schedule 30.10.2012    source источник
comment
+1 Красиво представленный вопрос.   -  person Thomas Matthews    schedule 31.10.2012


Ответы (1)


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

Редактировать: тогда вам просто нужно будет свернуть свой собственный эквивалент std::function, который поддерживает более 10 аргументов.

person Puppy    schedule 30.10.2012
comment
Я забыл упомянуть. Я попытался перейти на std::function, но моя исходная функция имеет более 10 аргументов (это подпрограмма Fortran). - person Abel Siqueira; 30.10.2012
comment
@AbelSiqueira: Получите лучший компилятор (или добавьте флаг -std=c++11): C++ 2011 поддерживает переменные аргументы, и вы можете передавать столько аргументов, сколько хотите. Если это не работает для вас, просто введите версию std::function<Signature> с подходящим количеством аргументов. - person Dietmar Kühl; 31.10.2012
comment
Указатели функций имеют свое время и место. Они не всегда могут быть лучшим решением, но в определенных ситуациях без них точно не обойтись. - person Adam Rosenfield; 31.10.2012
comment
@DietmarKühl: Извините, я не смог найти, что такое std::function<Signature> или как он работает. Можете ли вы указать направление или показать пример? Нельзя использовать -std=c++11 или менять компиляторы. - person Abel Siqueira; 31.10.2012
comment
@Siqueira: std::function<T> не является чем-то глубоко волшебным (я использовал Signature, чтобы указать, что аргумент шаблона на самом деле является сигнатурой функции). Вы можете реализовать ту же логику и предоставить столько аргументов, сколько вам нужно. В этом нет ничего сложного, просто в основном упражнение по набору текста. - person Dietmar Kühl; 31.10.2012
comment
@DietmarKühl: я сделал std::function<void(...)> с 13 аргументами. Чтобы определить функцию, которая обрабатывает 13 аргументов, достаточно 3 строк в заголовке. Но я не смог привязать 13 аргументов и не смог найти простой способ реализовать привязку для 13 аргументов. - person Abel Siqueira; 31.10.2012
comment
@DietmarKühl: в итоге я создал промежуточную функцию с 8 аргументами, первый из которых был массивом с избытком. В этих функциях я вызываю нужную мне функцию. Спасибо. - person Abel Siqueira; 31.10.2012