Как создать указатель на функцию из функции с фиксированными параметрами в C ++ - PullRequest
4 голосов
/ 17 августа 2011

Предположим, я хочу вызвать функцию f, которая запрашивает указатель на функцию типа

int (*foo)(int)

Эта функция похожа на

double f( int (*foo)(int))

У меня есть функция, которая делает это,но он требует другого объекта.

int bar(int, MyClass*)

Этот объект создается во время выполнения, скажем,

int main()
    // ... read some file / input
    MyClass myclass = MyClass(input);

    // Now pass the function pointer
    double retval = f( bar( int, &myclass))

Есть ли способ реализовать последнюю строку?Я ищу ответ без глобальных объектов.

Ответы [ 4 ]

4 голосов
/ 17 августа 2011

номер

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

2 голосов
/ 17 августа 2011

Изменить , чтобы учесть тот факт, что f нельзя изменить:

Поскольку вы не можете изменить f, единственное решение, которое я могу придумать, - это использовать какую-то глобальную переменную вместе с функцией пересылки:

int bar(int i, MyClass * mc);

namespace bar_parameters
{
    MyClass * mc = 0;
}

int binded_bar(int i)
{ 
    return bar(i, bar_parameters::mc);
}

int main()
{
    MyClass myclass(input);

    bar_parameters::mc = &myclass;
    double retval = f(binded_bar);
}

Это довольно некрасиво, однако ...


Невозможно связать параметры функции. Однако это возможно с помощью функтора (объекта функции).

Первое, что вам нужно сделать, это изменить параметр f, чтобы он стал функциональным объектом, таким как std::function или boost::function, вместо указателя функции:

double f(std::function<int (int)> foo);

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

После этого вы можете использовать методы привязки, такие как std/boost::bind (или bind1st / bind2nd в C ++ 03):

double retval = f(std::bind(bar, _1, &myclass));

Если у вас нет доступа к Boost или C ++ 0x, вы можете использовать обычную технику, заключающуюся в создании шаблона параметра для принятия любых вызываемых объектов (указателей на функции и функторов):

template <typename Func>
double f(Func foo);

Недостаток этого подхода заключается в том, что подпись функции применяется только при использовании ее в f.

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

double retval = f(std::bind2nd(std::ptr_fun(bar), &myclass));
1 голос
/ 17 августа 2011

В вашем случае вам придется делать что-то злое, например:

MyClass* g_myclass = 0;
int my_f(int i) {
    return bar(i, g_myclass);
}

int main()
    // ... read some file / input
    MyClass myclass = MyClass(input);

    // Now pass the function pointer
    g_myclass = &myclass;
    double retval = f(&my_f);
    g_myclass = 0; // no longer needed (you hope).
    ...
}

YMMV. Если указатель функции, который вы передаете, сохраняется где-то и используется снова позже, вы будете в беде. В этом случае вам понадобится список статических функций и глобальный массив указателей на MyClass и выделение / освобождение из этого списка. как необходимый. Вы можете генерировать функции, используя рекурсивную шаблонную метапрограмму. Не красиво, но возможно.

ИЛИ, выясните, как сгенерировать требуемый ассемблер для связывания, и закодируйте указатель MyClass в инструкциях по сборке. Затем функция размещается в куче. Не очень портативный, но также должен быть выполнимым. Я никогда не пробовал это, хотя и не могу гарантировать, что это будет работать или будет легко сделать. Код также выглядел бы отвратительно.

1 голос
/ 17 августа 2011

Вы можете добиться чего-то подобного, используя Boost.Bind .Теперь это является частью стандарта C ++ и может даже быть доступно с вашим компилятором.Обратите внимание, что Boost.Bind по-прежнему требует изменения подписи f.Если это невозможно, вы могли бы сделать что-то вроде

MyClass* _only_to_be_used_by_ugly;

int ugly(int i) {
  return bar(i, _only_to_be_used_by_ugly)
}

int main() {
  _only_to_be_used_by_ugly = MyClass(input);
  double retval = f(ugly);
}

Но я бы первым посоветовал против подобного подхода.

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