Возможен ли указатель на функцию с неизвестным количеством параметров? - PullRequest
2 голосов
/ 11 февраля 2011

Я пишу простой класс для измерения производительности функции с точки зрения времени.Пользователь должен иметь возможность отправить указатель на свою функцию, параметры функции, время вызова функции, и я вызову функцию, вернув истекшее время.Здесь моя проблема в том, что я не знаю, сколько параметров принимает пользовательская функция!Я думал использовать переменные функции для получения неизвестного числа параметров, однако у меня все еще есть проблема объявления указателя функции, который пользователь передает в качестве параметра (потому что у него нет постоянного номера переменной), и не зная типы переменных, которые я получаю, используяпеременная функция.

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

Есть ли способ решить эти проблемы или?

Ответы [ 4 ]

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

Не имеет смысла иметь указатель на функцию с неизвестными аргументами.Если вы не знаете, сколько существует аргументов (не говоря уже о их типах), как вы собираетесь заполнять аргументы во время выполнения?

Лучшее, что вы можете сделать, это потребовать, чтобы все функции пользователя выполнялисьиметь тот же прототип, а именно взять va_list в качестве параметра и потребовать, чтобы ваша библиотека предоставила вашей библиотеке тот же va_list (см. также http://c -faq.com / varargs / handoff.html ).

Например:

// Function-pointer type
typedef void (*func_t)(int, va_list);

// Your timer library function
void timer(func_t *p_func, ...)
{   
    va_list arg;
    va_start(arg, fmt);
    p_func(0, arg);
    va_end(arg);
}


// User's function
void user_function(int first_arg, va_list args)
{
   ...
};

// Invoke the timer library function
timer(&user_function, arg1, arg2, arg3);
3 голосов
/ 11 февраля 2011

Я думаю лямбда-функции могут быть использованы для этого:

template< typename Func >
unsigned int measure(Func f)
{
  // take time
  f();
  // take time
  return result;
}

void test_func_1(int i)            { std::cout << i; }
void test_func_2(std::ostream& os) { os << 42; }

int main()
{
  auto lambda_func_1 = [](){ test_func_1(42); };
  const unsigned int time_1 = measure( lambda_func_1 );
  std::cout << "calling test_func_1(42) took " << time_1 << " <unit>\n";

  auto lambda_func_2 = [](){ test_func_2(std::cerr); };
  const unsigned int time_2 = measure( lambda_func_2 );
  std::cout << "calling test_func_2(std::cout) took " << time_2 << " <unit>\n";

  return 0;
}

Конечно, лямбда-функции станут частью C ++ только после выпуска следующего стандарта (надеюсь, в этом году), но довольно много компиляторов (среди них GCC и VC) уже реализуют их, поэтому у вас есть шанс сделать это сюда.


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

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

Если вам нужно решение, которое работает без необходимости использования шаблонов с переменным числом аргументов или лямбда-выражений (для которых вам придется ждать предстоящего стандартного выпуска), вы можете использовать boost::bind, чтобы превратить функцию в такую, которая не принимает параметры путемПредварительная привязка всех параметров к функции:

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

template <typename Func>
int time_call(Func f) {
  int start_time = some_get_current_time(); //record the start time
  f(); // call the function with no parameters
  return some_get_current_time() - start_time; //return the time difference.
};

void my_algorithm(int x, float w, std::string s) {
  std::cout << x << w << s << std::endl;
};

int main() {
  int time_taken = time_call(boost::bind(&my_algorithm, 42, 0.3, "Hello World!"));
  std::cout << "The function took " << time_taken << " time-units to execute!" << std::endl;
  return 0;
};

Вышеприведенное будет работать на всех компиляторах и не требует каких-либо функций C ++ 0x.Также обратите внимание, что при вызове boost::bind вы можете поместить любые переменные (они не должны быть буквальными константами).И, кроме того, boost::bind может работать и с указателем на функции-члены.

РЕДАКТИРОВАТЬ: Если вам интересно, как boost::bind может сделать это без шаблонов с переменным числом, ну, это просто, они просто сделали один шаблон функцииперегрузка для всех возможных чисел параметров (я думаю, что предел по умолчанию составляет 10 параметров, но он может быть расширен).

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

К сожалению, текущий C ++ требует от вас написания набора шаблонов различной длины, по одному для каждого возможного количества аргументов.В принципе, C ++ 0x позволит вам использовать шаблоны с переменными числами, например, так:

template<typename Rv, typename Wrapper, typename... Args>
struct impl_wrapper {
    std::function<Rv (Args...)> func;

    Rv operator()(Args... args) const {
        Wrapper w;
        return func(args...);
    }

    impl_wrapper(const  std::function<Rv (Args...)> f)
        : func(f)
    {
    }
};

template<typename Wrapper>
struct wrap_func_ {
    template<typename Rv, typename... Args>
        impl_wrapper<Rv, Args...> operator()(const std::function<Rv (Args...)> &f)
        {
            return impl_wrapper<Rv, Wrapper, Args...>(f);
        }
};

template<typename Wrapper>
static wrap_func_<Wrapper> wrap_func;



struct test_wrapper {
    test_wrapper() {
        std::cout << "Begin call!\n";
    }

    ~test_wrapper() {
        std::cout << "End call!\n";
    }
};

int test_call(int x, char *y) {
    std::cout << y << x << std::endl;
    return x + 1;
}

int main() {
    std::function<int (int, char *)> f = test_call;
    f = wrap_func<test_wrapper>(f);

    std::cout << "Returned: " << f(42, "Prior to increment: ") << std::endl;
    return 0;
}

Однако это требует поддержки функций, еще не реализованных в G ++, и, скорее всего, в любом другом существующем компиляторе C ++:

test.cpp:21: sorry, unimplemented: cannot expand ‘Args ...’ into a fixed-length argument list

Следовательно, вместо каждого допустимого количества аргументов вы должны использовать перегрузку шаблона, вплоть до некоторого разумного максимума.

...