我正在编写一个简单的类来测量函数在时间方面的性能。用户应该能够发送一个指向他的函数的指针,函数的参数,调用函数的时间,我将调用函数,返回经过的时间。这里我的问题是我不知道用户的功能需要多少参数!我想使用可变参数函数来获取未知数量的参数,但是我仍然有一个问题,即声明用户作为参数传递的函数指针(因为它没有常量变量号)并且不知道我通过使用接收的变量类型可变函数。
它应该不难,我猜:)我想做的就是调用一个函数,这个函数不是我用函数指针定义的。
有没有办法解决这些问题或者?
答案 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
因此,您必须为每个可能的参数计数使用模板重载,最多可达到一些合理的最大值。