Передача аргументов указателя функции с повышением

Можно ли упростить/улучшить следующую передачу указателя функции с помощью boost::function и/или boost::bind?

void PassPtr(int (*pt2Func)(float, std::string, std::string))
{
   int result = (*pt2Func)(12, "a", "b"); // call using function pointer
   cout << result << endl;
}

// execute example code
void Pass_A_Function_Pointer()
{
   PassPtr(&DoIt);
}

person pingu    schedule 10.03.2013    source источник
comment
Это зависит от того, какой ответ вы ищете! Конечно, все вышеперечисленное можно упростить до cout << DoIt(12, "a", "b") << endl;, но я думаю, это не то, что вам нужно. Тем не менее, здесь нет очевидных возможностей воспользоваться функциями Boost, поскольку вы вызываете чистый указатель на функцию, а не объект функции и т. д.   -  person Oliver Charlesworth    schedule 10.03.2013
comment
Его можно улучшить тем, что вы можете использовать любую функцию-член или лямбда-выражения (если у вас есть совместимый с С++ 11 компилятор), а не только обычную функцию или статическую функцию-член. И, конечно же, с помощью boost::bind (или std::bind) вы можете привязать любой количество аргументов перед передачей объекта функции.   -  person Some programmer dude    schedule 10.03.2013
comment
Что исправлено и что можно изменить? Если подписи PassPtr и DoIt фиксированы, то нет, код настолько прост, насколько это возможно.   -  person CB Bailey    schedule 10.03.2013
comment
@Charles - все, что исправлено, это то, что я хочу передать указатель на функцию, например DoIt, и вызвать ее. Все остальное может измениться.   -  person pingu    schedule 10.03.2013
comment
Если вы хотите передать указатель на функцию, вы делаете это самым простым способом.   -  person CB Bailey    schedule 10.03.2013


Ответы (2)


Вы можете использовать boost::function<>, чтобы сделать возможным использование различных типов вызываемых объектов в качестве входных данных функции.

Далее следует пример с использованием C++11 (см. примечания после этого примера). Вот как бы вы переписали свою функцию:

#include <functional>
#include <string>
#include <iostream>

void PassFxn(std::function<int(float, std::string, std::string)> func)
//           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
   int result = func(12, "a", "b"); // call using function object
   std::cout << result << std::endl;
}

Вот пара функций для проверки:

int DoIt(float f, std::string s1, std::string s2)
{
    std::cout << f << ", " << s1 << ", " << s2 << std::endl;
    return 0;
}

int DoItWithFourArgs(float f, std::string s1, std::string s2, bool b)
{
    std::cout << f << ", " << s1 << ", " << s2 << ", " << b << std::endl;
    return 0;
}

struct X
{
    int MemberDoIt(float f, std::string s1, std::string s2)
    {
        std::cout << "Member: " << f << ", " << s1 << ", " << s2 << std::endl;
        return 0;
    }

    static int StaticMemberDoIt(float f, std::string s1, std::string s2)
    {
        std::cout << "Static: " << f << ", " << s1 << ", " << s2 << std::endl;
        return 0;
    }
};

А вот и тестовая процедура:

int main()
{
    PassFxn(DoIt); // Pass a function pointer...

    // But we're not limited to function pointers with std::function<>...

    auto lambda = [] (float, std::string, std::string) -> int
    {
        std::cout << "Hiho!" << std::endl;
        return 42;
    };

    PassFxn(lambda); // Pass a lambda...

    using namespace std::placeholders;
    PassFxn(std::bind(DoItWithFourArgs, _1, _2, _3, true)); // Pass bound fxn

    X x;
    PassFxn(std::bind(&X::MemberDoIt, x, _1, _2, _3)); // Use a member function!

    // Or, if you have a *static* member function...
    PassFxn(&X::StaticMemberDoIt);

    // ...and you can basically pass any callable object!
}

А вот и живой пример.

ПРИМЕЧАНИЯ:

Вы можете легко изменить std::function<> на boost::function<> и std::bind<> на boost::bind<>, если вы работаете с C++03 (фактически, Boost.Function вдохновил std::function<> и позже стал частью стандартной библиотеки C++). В этом случае вместо включения заголовка <functional> вам придется включить заголовки boost/function.hpp и boost/bind.hpp (последний только в том случае, если вы хотите использовать boost::bind).

В качестве еще одного примера, который должен дать вам представление о силе, которую std::function<>/boost::function<> дает вам благодаря своей способности инкапсулировать любой вызываемый объект, см. также эти вопросы и ответы на StackOverflow.

person Andy Prowl    schedule 10.03.2013
comment
Замечательно! Не могли бы вы добавить еще один пример, прежде чем я отмечу ответ. Как будет выглядеть синтаксис, когда DoIt является переданной функцией-членом? - person pingu; 10.03.2013
comment
@pingu: Рад, что помог. Я также обновил живой пример, если вы хотите взглянуть. - person Andy Prowl; 10.03.2013

Я предполагаю, что вы хотите улучшить функциональность PassPtr, а не весь приведенный вами пример кода. Если вы используете С++ 11 и можете использовать лямбда-выражения, я бы упростил его до:

template <typename Func>
void PassPtr(Func f) {
  std::cout << f(12, "a", "b") << std::endl;
}

Это позволит передать любой вызываемый объект как f. Причина, по которой функция принимается выведенным типом шаблона, состоит в том, чтобы разрешить встраивание любых передаваемых лямбда-выражений. Конечно, это не навязывает какую-либо конкретную подпись переданной функции (или даже то, что она должна быть вызываемым объектом). Например, если вы передадите int, вы получите несколько запутанных ошибок компилятора.

Альтернатива, используя [boost|std]::function, состоит в том, чтобы сделать это:

void PassPtr(std::function<int(float, std::string, std::string)> f) {
  std::cout << f(12, "a", "b") << std::endl;
}

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

person Joseph Mansfield    schedule 10.03.2013