Можно ли сохранить функцию (указатель?) В объект? - PullRequest
5 голосов
/ 03 февраля 2011

Я искал это, но мне кажется, я просто запутался.

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

Я имел в виду конструктор, который будет принимать указатель на функцию и параметры, которые будут переданы указателю на эту функцию. Этот объект также будет иметь метод run (), который будет запускать указанный указатель функции, и метод wait_until_completed (), который блокирует до тех пор, пока функция не будет запущена.

Указатель на функцию должен быть функцией от другого объекта, если это имеет смысл. Например

Foo::*Bar(int);

У меня есть wait_until_completed (), работающий с использованием pthread_cond_t, но я застрял на этом указателе функции и чувствую, что я просто бегаю кругами.

Любой совет?

РЕДАКТИРОВАТЬ: Это для школы (любое мое общее понимание), поэтому сторонние библиотеки не будут работать: /

Мне показалось, что я очень плохо объяснил это, чтобы дать мне пример кода (исключая все вещи для синхронизации)

class Foo
{
public:
    Foo(void (Bar::*function) (int), int function_param)
    {
        // Save the function pointer into this object
        this->function = &function;

        // Save the paramater to be passed to the function pointer in this object
        param = function_param;
    }

    run()
    {
        (*function)(param);
    }

private:
    // The function pointer
    void (Bar::*function) (int) = NULL;

    // The paramater to pass the function pointer
    int param;
}

Это в двух словах, что я пытаюсь сделать. Тем не менее, я не уверен, что это синтаксис или я тупой, но я не могу понять, как на самом деле это сделать и заставить его скомпилировать.

Thoughs? И спасибо за все советы до сих пор:)

Ответы [ 6 ]

9 голосов
/ 03 февраля 2011

Во-первых, вы захотите typedef ваш тип функции, упрощая его повторное использование и уменьшая вероятность ошибок.

typedef void (Bar::*function_type)(int);

Теперь вы можете использовать typedef следующим образом:

Foo(function_type func, int func_param)
    : function_(func)
    , param_(func_param)
{
}

Кроме того, рекомендуется использовать список инициализации для инициализации переменных-членов (вы можете найти больше о списках инициализации здесь ).
Однако вы все равно не сможетевызвать функцию.Функции-члены класса, также называемые связанные функции , ДОЛЖНЫ использоваться с экземпляром объекта, поскольку они связаны с ними.Ваша run функция должна выглядеть следующим образом (вы также забыли тип возвращаемого значения):

void run(Bar* object){
  (object->*function_(param_));
}

Она использует специальный оператор ->* для вызова функций-членов через указатель на функцию-член.
Кроме того, вы не можете инициализировать большинство переменных непосредственно внутри класса (только константы статических интегралов, такие как static const int i = 5;).Теперь вы сможете создавать экземпляр объекта Foo и вызывать для него функцию запуска.
Здесь у вас есть полностью компилируемый пример:

#include <iostream>
using namespace std;

class Bar{
public:
    void MyBarFunction(int i){
        cout << i << endl;
    }
};

class Foo
{
public:
    typedef void (Bar::*function_type)(int);

    Foo(Bar* object, function_type function, int function_param)
        : object_(object)  // save the object on which to call the function later on
        , function_(function)  // save the function pointer
        , param_(function_param)  // save the parameter passed at the function
    {
    }


    void Run()
    {
        (object_->*function_)(param_);
    }

private:
    // The object to call the function on
    Bar* object_;

    // The function pointer
    function_type function_;

    // The paramater to pass the function pointer
    int param_;
};

int main(void){
    // create a Bar object
    Bar bar;
    // create a Foo object, passing the Bar object
    // and the Bar::myBarFunction as a parameter
    Foo foo(&bar, &Bar::MyBarFunction, 5);

    // call Bar::MyBarFunction on the previously passed Bar object 'bar'
    foo.Run();

    cin.get();
    return 0;
}

Это может быть немного сложным за раз, но я надеюсь, что это поможет вам понять указатели на функции-члены и как их использовать.:)

4 голосов
/ 03 февраля 2011

Да, вы можете сохранить указатель функции:

struct simple_callable_object
{
    void (*f_)();
    simple_callable_object(void (*f)()) : f_(f) { }

    void operator()() { f_(); }
};

Этот шаблон обобщен в оболочке полиморфной функции function и в связывателе аргументов bind, которую можно найти в Boost, C ++ TR1 и C ++ 0x.

Шаблон запуска функции асинхронно и возможности блокировать ее и ждать, пока она не завершится, называется «будущим». Библиотека поддержки потоков C ++ 0x имеет std::future и std::async, которые облегчают эту задачу. Например:

// pretend 'f' is a long-running task that you want to run asynchronously:
int f(int x) { return x * 2; }

void g()
{
    std::future<int> fi = std::async(f, 21);

    // do other meaningful work

    int x = fi.get(); // blocks until the async task completes
}

Вам не нужно использовать std::async, чтобы получить std::future; Вы можете написать пул потоков или другое средство, которое асинхронно завершает задачи и позволяет одному из них генерировать и возвращать фьючерсы.

3 голосов
/ 03 февраля 2011

Похоже, вы пытаетесь заново изобрести шаблон "команды". Локи (среди многих других) должен дать разумный старт (хотя вам, вероятно, придется добавить синхронизацию потоков самостоятельно).

2 голосов
/ 03 февраля 2011

Использование функции повышения объекта.

РЕДАКТИРОВАТЬ: вот тривиальный пример:

#include <iostream>
#include <boost/function.hpp>

struct X {
  int foo(int a, int b) { return a * b; }
};

int main(void)
{
  boost::function<int (X*, int, int)> func;
  func = &X::foo;

  X x;
  std::cout << func(&x, 1, 2) <<std::endl;

  return 0;
}
1 голос
/ 03 февраля 2011

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

Итак, удалите эту строку, и ваш пример должен работать отлично, как я могу сказать.Вы должны предпочесть инициализаторы присваиванию в теле конструктора.Поэтому это должно выглядеть так:

Foo(void (Bar::*fun) (int), int fparam) : function(fun), param(fparam) {}

Редактировать: Ооо, я что-то пропустил в вашем ОП.Выше вы принимаете указатель на функцию member в качестве параметра, но пытаетесь вызвать его как нормальную функцию.Исправленная форма будет использовать void (*)(int) в качестве типа, а не void (Bar::*)(int).

0 голосов
/ 03 февраля 2011

И если вы не прислушиваетесь к совету Джерри, я бы предложил использовать Функторы вместо того, чтобы возиться с привередливыми указателями на функции.

http://en.wikipedia.org/wiki/Function_object

Однако я рекомендую ответ Джерри.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...