如何编写一个可以包装任何函数的包装器,并且可以像函数本身一样调用它?
我需要这个的原因:我想要一个Timer对象,它可以包装一个函数,就像函数本身一样,加上它记录所有调用的累计时间。
场景看起来像这样:
// a function whose runtime should be logged
double foo(int x) {
// do something that takes some time ...
}
Timer timed_foo(&foo); // timed_foo is a wrapping fct obj
double a = timed_foo(3);
double b = timed_foo(2);
double c = timed_foo(5);
std::cout << "Elapsed: " << timed_foo.GetElapsedTime();
如何撰写此Timer
课程?
我正在尝试这样的事情:
#include <tr1/functional>
using std::tr1::function;
template<class Function>
class Timer {
public:
Timer(Function& fct)
: fct_(fct) {}
??? operator()(???){
// call the fct_,
// measure runtime and add to elapsed_time_
}
long GetElapsedTime() { return elapsed_time_; }
private:
Function& fct_;
long elapsed_time_;
};
int main(int argc, char** argv){
typedef function<double(int)> MyFct;
MyFct fct = &foo;
Timer<MyFct> timed_foo(fct);
double a = timed_foo(3);
double b = timed_foo(2);
double c = timed_foo(5);
std::cout << "Elapsed: " << timed_foo.GetElapsedTime();
}
(顺便说一句,我知道gprof
以及其他用于分析运行时的工具,但是有一个Timer
对象来记录一些选定函数的运行时对我来说更方便。)
答案 0 :(得分:10)
基本上,在当前的C ++中,你想做的事情是不可能的。对于你想要包装的任何数量的函数,你需要通过
重载const reference
non-const reference
但是它仍然没有完美转发(一些边缘案例仍然存在),但它应该合理地运作。如果你将自己局限于const引用,你可以使用这个(未经测试):
template<class Function>
class Timer {
typedef typename boost::function_types
::result_type<Function>::type return_type;
public:
Timer(Function fct)
: fct_(fct) {}
// macro generating one overload
#define FN(Z, N, D) \
BOOST_PP_EXPR_IF(N, template<BOOST_PP_ENUM_PARAMS(N, typename T)>) \
return_type operator()(BOOST_PP_ENUM_BINARY_PARAMS(N, T, const& t)) { \
/* some stuff here */ \
fct_(ENUM_PARAMS(N, t)); \
}
// generate overloads for up to 10 parameters
BOOST_PP_REPEAT(10, FN, ~)
#undef FN
long GetElapsedTime() { return elapsed_time_; }
private:
// void() -> void(*)()
typename boost::decay<Function>::type fct_;
long elapsed_time_;
};
请注意,对于返回类型,您可以使用boost的函数类型库。然后
Timer<void(int)> t(&foo);
t(10);
您也可以使用纯值参数重载,然后如果您想通过引用传递内容,请使用boost::ref
。这实际上是一种非常常见的技术,特别是当这些参数将被保存时(这种技术也用于boost::bind
):
// if you want to have reference parameters:
void bar(int &i) { i = 10; }
Timer<void(int&)> f(&bar);
int a;
f(boost::ref(a));
assert(a == 10);
或者您可以为const和非const版本添加这些重载,如上所述。查看Boost.Preprocessor以了解如何编写正确的宏。
你应该意识到如果你想能够传递任意的callables(不仅仅是函数),整个事情会变得更加困难,因为你需要一种方法来获得他们的结果类型(这并不是那么简单) 。 C ++ 1x将使这种东西变得更容易。
答案 1 :(得分:9)
这是一种 easy 包装函数的方法。
template<typename T>
class Functor {
T f;
public:
Functor(T t){
f = t;
}
T& operator()(){
return f;
}
};
int add(int a, int b)
{
return a+b;
}
void testing()
{
Functor<int (*)(int, int)> f(add);
cout << f()(2,3);
}
答案 2 :(得分:6)
我认为您需要将其用于测试目的,并且不会将它们用作真正的代理或装饰器。因此,您不需要使用operator(),并且可以使用任何其他更方便的调用方法。
template <typename TFunction>
class TimerWrapper
{
public:
TimerWrapper(TFunction function, clock_t& elapsedTime):
call(function),
startTime_(::clock()),
elapsedTime_(elapsedTime)
{
}
~TimerWrapper()
{
const clock_t endTime_ = ::clock();
const clock_t diff = (endTime_ - startTime_);
elapsedTime_ += diff;
}
TFunction call;
private:
const clock_t startTime_;
clock_t& elapsedTime_;
};
template <typename TFunction>
TimerWrapper<TFunction> test_time(TFunction function, clock_t& elapsedTime)
{
return TimerWrapper<TFunction>(function, elapsedTime);
}
因此,为了测试你的某些功能,你应该只使用test_time
函数而不是直接TimerWrapper
结构
int test1()
{
std::cout << "test1\n";
return 0;
}
void test2(int parameter)
{
std::cout << "test2 with parameter " << parameter << "\n";
}
int main()
{
clock_t elapsedTime = 0;
test_time(test1, elapsedTime).call();
test_time(test2, elapsedTime).call(20);
double result = test_time(sqrt, elapsedTime).call(9.0);
std::cout << "result = " << result << std::endl;
std::cout << elapsedTime << std::endl;
return 0;
}
答案 3 :(得分:2)
Stroustrup演示了一个函数包装(injaction)技能,使operator->
重载。关键的想法是:operator->
将重复调用,直到它遇到本机指针类型,所以让Timer::operator->
返回一个临时对象,临时对象返回它的指针。接下来会发生:
你可以在ctor和dtor中注入任何代码。像这样。
template < class F >
class Holder {
public:
Holder (F v) : f(v) { std::cout << "Start!" << std::endl ; }
~Holder () { std::cout << "Stop!" << std::endl ; }
Holder* operator->() { return this ; }
F f ;
} ;
template < class F >
class Timer {
public:
Timer ( F v ) : f(v) {}
Holder<F> operator->() { Holder<F> h(f) ; return h ; }
F f ;
} ;
int foo ( int a, int b ) { std::cout << "foo()" << std::endl ; }
int main ()
{
Timer<int(*)(int,int)> timer(foo) ;
timer->f(1,2) ;
}
实施和使用都很简单。
答案 4 :(得分:2)
如果你看一下你所包含的std :: tr1 :: function的实现,你可能会找到答案。
在c ++ 11中,std :: function是使用可变参数模板实现的。使用这样的模板,您的计时器类看起来像
template<typename>
class Timer;
template<typename R, typename... T>
class Timer<R(T...)>
{
typedef R (*function_type)(T...);
function_type function;
public:
Timer(function_type f)
{
function = f;
}
R operator() (T&&... a)
{
// timer starts here
R r = function(std::forward<T>(a)...);
// timer ends here
return r;
}
};
float some_function(int x, double y)
{
return static_cast<float>( static_cast<double>(x) * y );
}
Timer<float(int,double)> timed_function(some_function); // create a timed function
float r = timed_function(3,6.0); // call the timed function
答案 5 :(得分:2)
使用宏和模板的解决方案:例如,您想要换行
double foo( double i ) { printf("foo %f\n",i); return i; }
double r = WRAP( foo( 10.1 ) );
在调用foo()之前和之后,应该调用包装函数beginWrap()和endWrap()。 (使用endWrap()作为模板函数。)
void beginWrap() { printf("beginWrap()\n"); }
template <class T> T endWrap(const T& t) { printf("endWrap()\n"); return t; }
宏
#define WRAP(f) endWrap( (beginWrap(), f) );
使用逗号运算符的优先级来确保首先调用beginWrap()。 f的结果传递给endWrap(),它只返回它。 所以输出是:
beginWrap()
foo 10.100000
endWrap()
结果r包含10.1。
答案 6 :(得分:1)
我不太清楚你在寻找什么..但是,对于给定的例子,它只是:
void operator() (int x)
{
clock_t start_time = ::clock(); // time before calling
fct_(x); // call function
clock_t end_time = ::clock(); // time when done
elapsed_time_ += (end_time - start_time) / CLOCKS_PER_SEC;
}
注意:这将以秒为单位测量时间。如果您想拥有高精度计时器,您可能需要检查特定于操作系统的功能(如Windows上的GetTickCount或QueryPerformanceCounter)。
如果你想拥有一个通用的函数包装器,你应该看一下Boost.Bind,这将有助于大力推进。
答案 7 :(得分:1)
如果您希望创建一个可以包装和调用任意函数的泛型类,那么您将面临一个巨大的挑战。在这种情况下,您必须使functor(operator())返回double并将int作为参数。然后,您创建了一个类系列,可以使用相同的签名调用所有函数。只要您想添加更多类型的功能,您需要更多该签名的仿函数,例如
MyClass goo(double a, double b)
{
// ..
}
template<class Function>
class Timer {
public:
Timer(Function& fct)
: fct_(fct) {}
MyClass operator()(double a, double b){
}
};
编辑:一些拼写错误
答案 8 :(得分:1)
如果你的编译器支持可变参数宏,我试试这个:
class Timer {
Timer();// when created notes start time
~ Timer();// when destroyed notes end time, computes elapsed time
}
#define TIME_MACRO(fn, ...) { Timer t; fn(_VA_ARGS_); }
所以,要使用它,你会这样做:
void test_me(int a, float b);
TIME_MACRO(test_me(a,b));
这是关闭的,你需要四处寻找返回类型才能工作(我认为你必须在TIME_MACRO调用中添加一个类型名称,然后让它生成一个临时变量)。
答案 9 :(得分:0)
在C ++函数中是一等公民,你可以将函数作为值传递。
因为你想要它取一个int并返回一个double:
Timer(double (*pt2Function)(int input)) {...
答案 10 :(得分:0)
以下是我使用函数指针而不是模板的方法:
// pointer to a function of the form: double foo(int x);
typedef double (*MyFunc) (int);
// your function
double foo (int x) {
// do something
return 1.5 * x;
}
class Timer {
public:
Timer (MyFunc ptr)
: m_ptr (ptr)
{ }
double operator() (int x) {
return m_ptr (x);
}
private:
MyFunc m_ptr;
};
我把它更改为不引用该函数,而只是一个普通的函数指针。用法保持不变:
Timer t(&foo);
// call function directly
foo(i);
// call it through the wrapper
t(i);