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

11

20

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.

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 Timer class?

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 gprof and other tools for profiling runtime, but having such a Timer object to log the runtime of a few selected functions is more convenient for my purposes.)

Philpot answered 18/5, 2009 at 19:38 Comment(1)
Does it have to be C++? If you don't mind "getting your hands dirty", you can probably hack something using C's varargs...Bloke
R
10

Here is an easy way 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);
}
Redoubt answered 18/5, 2009 at 20:13 Comment(2)
Great idea in general. But it doesn't allow measuring time, since no code can run after the function call is completed.Burroughs
Yes, you can run code only in operator(). Unfortunately C++ wasn't designed to support functional style. In order to be able to do magic you have to work around the restrictions. But, hey, that's one of the reasons why we like programming :)Redoubt
B
11

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

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):

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

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):

// 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.Preprocessor for how to write the proper macros.

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.

Burroughs answered 18/5, 2009 at 20:29 Comment(3)
Great! I just tried to remember enable_if stuff and was going to implement this without macros (which I wasn't aware). You saved my time. Thanks.Kvass
As you hinted at, with C++0x (or 1x I guess) perfect forwarding gets really easy.Goering
I'm not sure how to ask this in a polite way, but could you update this with the "way easier" method available in C++1{1,4,7}?Adsorbate
R
10

Here is an easy way 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);
}
Redoubt answered 18/5, 2009 at 20:13 Comment(2)
Great idea in general. But it doesn't allow measuring time, since no code can run after the function call is completed.Burroughs
Yes, you can run code only in operator(). Unfortunately C++ wasn't designed to support functional style. In order to be able to do magic you have to work around the restrictions. But, hey, that's one of the reasons why we like programming :)Redoubt
K
7

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.

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_time function and not the direct TimerWrapper structure

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;
}
Kvass answered 18/5, 2009 at 21:7 Comment(4)
+1, since this is a good approach imho. Just something to be aware of: the timer object is destroyed at the end of the full expression. So if you do f(test_time(test1, elapsedTime).call(20)); The time will include the one of "f" too. Just so you know. Since it's only for testing purposes, it probably won't matter since one can avoid it.Burroughs
You could use surrogate functions too, if you want. Then it will look like this: test_time(test2, elapsedTime)(20); for that, TimerWrapper<TFunction> needs this one: operator TFunction() { return call; } now, if you write "(20)", the compiler will convert the timer into the function-pointer and call it with the arguments. No nasty op() overloading like in my answer then :)Burroughs
+1. I like how you get around the whole forwarding problem by just exposing the function object member directly! I'd never have thought of that.Fatuity
@litb: I had never considered that a conversion function could convert to pointer-to-function type, or that the compiler would try those type conversions when it sees function-call syntax -- but it works! It seems like this approach actually goes some way to solving the forwarding problem in C++: you can put any "pre-call" code to run in the conversion function, we're just missing a place to put the "post-call" code. But using a temporary object's destructor is good enough for many cases. Any thoughts?Fatuity
E
5

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

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

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
Ebracteate answered 29/5, 2013 at 2:31 Comment(0)
N
3

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:

  1. temp obj created (ctor called).
  2. target function called.
  3. temp obj destructed (dtor called).

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

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.

Nullipore answered 19/5, 2009 at 5:10 Comment(2)
+1, yes this is a good approach. But as litb points out on Mykola Golubyev's answer, the temporary will be deleted at the end of the containing full-expression, which might be different than immediately after the call to f -- e.g. "big_slow_function(timer->f(1, 2))" will actually include the time for running big_slow_function() too. This is not a crippling fault, just something to be aware of.Fatuity
For reference, Stroustrup's original description can be found hereTheca
B
3

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.)

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()
foo 10.100000
endWrap()

And the result r contains 10.1.

Butchery answered 31/1, 2017 at 14:18 Comment(0)
L
1

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 GetTickCount or QueryPerformanceCounter on Windows).

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

Leverick answered 18/5, 2009 at 19:51 Comment(2)
I wasn't asking about how to measure runtime. I want a function object that can wrap any function and provide an operator() with the same signature as the function itself. Your operator() is hard-coded to take int and return void.Philpot
Thanks for adding the reference to Boost.Bind. With that it should be possible ...Philpot
F
1

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.

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

Feodor answered 18/5, 2009 at 19:53 Comment(1)
Note that the return value of the operator is only needed if you are interested in the actual result of the wrapped function, i.e. the operator above could just as well be a void.Feodor
M
1

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).

Mewl answered 18/5, 2009 at 20:52 Comment(0)
C
0

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

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

Timer(double (*pt2Function)(int input)) {...
Covenanter answered 18/5, 2009 at 19:52 Comment(1)
No, I do care about passing and returning values. The operator() should have the same signature as the original function (take int, return double in the case of this specific example.)Philpot
C
0

Here's how I'd do it, using a function pointer instead of a template:

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

I changed it to not take a reference to the function, but just a plain function pointer. Usage remains the same:

  Timer t(&foo);
  // call function directly
  foo(i);
  // call it through the wrapper
  t(i);
Cracked answered 18/5, 2009 at 20:30 Comment(1)
Thanks, but there is a problem: It is not generic. You've hardcoded it to work with a function that takes int and returns double. But the Timer should be generic and should work with any function.Philpot

© 2022 - 2024 — McMap. All rights reserved.