C++:行为就像函数本身一样的函数包装器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/879408/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:46:15  来源:igfitidea点击:

C++: Function wrapper that behaves just like the function itself

c++functionwrapperfunctional-programmingtr1

提问by Frank

How can I write a wrapper that can wrap any function and can be called just like the function itself?

如何编写一个可以包装任何函数并且可以像函数本身一样被调用的包装器?

The reason I need this: I want a Timer object that can wrap a function and behave just like the function itself, plus it logs the accumulated time of all its calls.

我需要这个的原因:我想要一个 Timer 对象,它可以包装一个函数并且表现得就像函数本身一样,而且它记录了所有调用的累积时间。

The scenario would look like this:

场景如下所示:

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

How can I write this Timerclass?

我该如何编写这个Timer类?

I am trying something like this:

我正在尝试这样的事情:

#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();
}

(BTW, I know of gprofand other tools for profiling runtime, but having such a Timerobject to log the runtime of a few selected functions is more convenient for my purposes.)

(顺便说一句,我知道gprof还有其他用于分析运行时的工具,但是有这样一个Timer对象来记录一些选定函数的运行时对我来说更方便。)

采纳答案by Nick Dandoulakis

Here is an easyway to wrap functions.

这是包装函数的简单方法。

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

回答by Johannes Schaub - litb

Basically, what you want to do is impossible in current C++. For any number of arity of function you want to wrap, you need to overload by

基本上,您想要做的事情在当前的 C++ 中是不可能的。对于您想要包装的任意数量的函数,您需要通过

const reference
non-const reference

But then it's still not perfectly forwarding (some edge cases still stand), but it should work reasonable well. If you limit yourself to const references, you can go with this one (not tested):

但是它仍然不是完美的转发(一些边缘情况仍然存在),但它应该可以正常工作。如果你限制自己使用 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_;
};

Note that for the return type, you can use boost's function types library. Then

请注意,对于返回类型,您可以使用 boost 的函数类型库。然后

Timer<void(int)> t(&foo);
t(10);

You can also overload using pure value parameters, and then if you want to pass something by reference, use boost::ref. That's actually a pretty common technique, especially when such parameters are going to be saved (this technique is also used for boost::bind):

您还可以使用纯值参数重载,然后如果您想通过引用传递某些内容,请使用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);

Or you can go and add those overloads for both const and non-const versions as explained above. Look into Boost.Preprocessorfor how to write the proper macros.

或者,您可以为常量和非常量版本添加这些重载,如上所述。查看Boost.Preprocessor以了解如何编写正确的宏。

You should be aware that the whole thing will become more difficult if you want to be able to pass arbitrary callables (not only functions), since you will need a way then to get their result type (that's not all that easy). C++1x will make this sort of stuff way easier.

您应该意识到,如果您希望能够传递任意可调用对象(不仅是函数),整个事情将变得更加困难,因为您将需要一种方法来获取它们的结果类型(这并不是那么容易)。C++1x 将使这种事情变得更容易。

回答by Mykola Golubyev

I assume you need this for test purpose and aren't going to use them as a real proxies or decorators. So you won't need to use operator() and can use any other more-less convenient method of call.

我假设您出于测试目的需要它,并且不会将它们用作真正的代理或装饰器。所以你不需要使用 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);
}

So to test some of yours function you should use only test_timefunction and not the direct TimerWrapperstructure

所以要测试你的一些函数,你应该只使用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;
}

回答by a.lasram

You may probably find an answer if you look at the implementation of std::tr1::function that you include.

如果您查看所包含的 std::tr1::function 的实现,您可能会找到答案。

In c++11, std:: function is implemented with variadic templates. Using such templates your timer class can look like

在 c++11 中,std:: 函数是用可变参数模板实现的。使用这样的模板,你的计时器类看起来像

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

回答by yoco

Stroustrup had demonstrated a function wrapper(injaction) skill with overloading the operator->. The key idea is: operator->will repeatly called until it meets a native pointer type, so let Timer::operator->return a temp object, and the temp object return its pointer. Then following will happen:

Stroustrup 展示了一个函数包装器(注入)技巧,重载operator->. 关键思想是:operator->会重复调用直到遇到原生指针类型,所以让我们Timer::operator->返回一个临时对象,临时对象返回它的指针。然后会发生以下情况:

  1. temp obj created (ctor called).
  2. target function called.
  3. temp obj destructed (dtor called).
  1. temp obj 创建(调用ctor)。
  2. 目标函数调用。
  3. temp obj 被破坏(dtor 被调用)。

And you can inject any code within the ctor and the dtor. Like this.

您可以在 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) ;
}

The implementation and the usage are both easy.

实现和使用都很容易。

回答by Joachim

A solution using macros and templates: For example you want to wrap

使用宏和模板的解决方案:例如您要包装

double foo( double i ) { printf("foo %f\n",i); return i; }
double r = WRAP( foo( 10.1 ) );

Before and after calling foo() the wrapper functions beginWrap() and endWrap() should be called. (With endWrap() being a template function.)

在调用 foo() 之前和之后,应该调用包装函数 beginWrap() 和 endWrap()。(endWrap() 是一个模板函数。)

void beginWrap() { printf("beginWrap()\n"); }
template <class T> T endWrap(const T& t) { printf("endWrap()\n"); return t; }

The macro

#define WRAP(f) endWrap( (beginWrap(), f) );

uses the precedence of the comma-operator to assure beginWrap() is called first. The result of f is passed to endWrap() which just returns it. So the output is:

使用逗号运算符的优先级来确保首先调用 beginWrap()。f 的结果传递给 endWrap() ,它只是返回它。所以输出是:

beginWrap()
foo 10.100000
endWrap()

And the result r contains 10.1.

结果 r 包含 10.1。

回答by Michael Kohne

If your compiler supports variadic macros, I'd try this:

如果你的编译器支持可变参数宏,我会试试这个:

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

So, to use it, you'd do this:

所以,要使用它,你会这样做:

void test_me(int a, float b);

TIME_MACRO(test_me(a,b));

That's off the cuff, and you'd need to play around to get return types to work (I think you'd have to add a type name to the TIME_MACRO call and then have it generate a temp variable).

这是袖手旁观,你需要玩弄返回类型的工作(我认为你必须添加一个类型名称到 TIME_MACRO 调用,然后让它生成一个临时变量)。

回答by ralphtheninja

You're out for a big challenge if you are looking to create a generic class that can wrap and call an arbitrary function. In this case you'd have to make the functor (the operator()) to return double and take an int as a parameter. Then you have created a family of classes that can call all functions with that same signature. As soon as you want to add more types of functions, you need more functors of that signature, e.g.

如果您希望创建一个可以包装和调用任意函数的泛型类,那么您将面临一个巨大的挑战。在这种情况下,您必须使函子(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){

  }

};

EDIT: Some spelling errors

编辑:一些拼写错误

回答by beef2k

It's not really clear to me for what you are looking.. However, for the given example, it's simply:

我不太清楚你在看什么。但是,对于给定的例子,它只是:

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

Note: This will measure the time in seconds. If you want to have high-precision timers, you probably have to check OS specific functionality (like GetTickCountor QueryPerformanceCounteron Windows).

注意:这将以秒为单位测量时间。如果您想要高精度计时器,您可能必须检查操作系统特定功能(如Windows 上的GetTickCountQueryPerformanceCounter)。

If you want to have a generic function wrapper, you should have a look on Boost.Bindthat will help tremendeously.

如果你想要一个通用的函数包装器,你应该看看Boost.Bind,这将有很大帮助。

回答by Tom

In C++ functions are first class citizens, you can literally pass a function as a value.

在 C++ 中,函数是一等公民,您可以直接将函数作为值传递。

Since you want it to take an int and return a double:

由于您希望它接受一个 int 并返回一个 double:

Timer(double (*pt2Function)(int input)) {...