是指向可能具有未知数量参数的函数的指针吗?

时间:2011-02-11 17:32:30

标签: c++ variadic-functions

我正在编写一个简单的类来测量函数在时间方面的性能。用户应该能够发送一个指向他的函数的指针,函数的参数,调用函数的时间,我将调用函数,返回经过的时间。这里我的问题是我不知道用户的功能需要多少参数!我想使用可变参数函数来获取未知数量的参数,但是我仍然有一个问题,即声明用户作为参数传递的函数指针(因为它没有常量变量号)并且不知道我通过使用接收的变量类型可变函数。

它应该不难,我猜:)我想做的就是调用一个函数,这个函数不是我用函数指针定义的。

有没有办法解决这些问题或者?

4 个答案:

答案 0 :(得分:4)

使用具有未知参数的函数的函数指针实际上没有意义。如果您不知道有多少参数(更不用说它们的类型),您将如何在运行时填写参数?

您可以做的最好的事情是要求用户的所有功能都具有相同的原型,即将va_list作为参数,并要求您的库为您的库提供相同的va_list(另见http://c-faq.com/varargs/handoff.html)。

e.g:

// 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);

答案 1 :(得分:3)

我认为 lambda函数 可用于执行此操作:

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;
}

当然,只有在下一个标准发布之后(希望今年),lambda函数才会成为C ++的一部分,但是很多编译器(其中包括GCC和VC)已经实现了它们,所以你有机会这样做这条路。


还可以制作一个使用可变参数模板参数和完美转发的函数模板,将要测量的函数的参数传递给测量函数,然后将它们传递给它们。但我没有玩过这个,所以我不能写出来。

答案 2 :(得分:2)

如果你想要一个无需可变参数模板或lambda的解决方案(你将不得不等待即将推出的标准版本),你可以使用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个参数,但它可以扩展)。

答案 3 :(得分:0)

不幸的是,当前的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

因此,您必须为每个可能的参数计数使用模板重载,最多可达到一些合理的最大值。