0

I am toying with this idea for a while but cant seem to wrap my head around it.

Basically what I want to do is create a general Timer class that times all the functions that are passed to it. Averaging it when the same function is called multiple times so it has to store it somehow. It should therefore use the function name to store the task and average it when it occurs more than once.

Pseudoish code of what it should look like.

Class FunctionTaks
{
    std::string d_name;
    double d_execution_time;
}

Class Timer
{
    private:
        std::vector<FunctionTask> d_tasks;

    public:
        template <typename Function, typename ReturnType>
        ReturnType time(Function f)
        {
            // check if function f is timed for first time
            // start timer             
            // run function f
            auto r = f.invoke();
            // stop timer  
            // store function name and time, average if more than once
            // return whatever f should return
            return r;
        }

        void report() const;
}

I dont really know how to do this, especially when Function f has a different amount of arguments.

Timer t;

t.time(foo());
t.time(bar());
t.time(foo());

t.report();

I basically have a few core issues.

  1. How to let a function wrapper return the same type that the injected code is suppose to return.
  2. How to obtain the function name that is being injected.
  3. The wrapper should not be limited by arguments passed on to the injected function. How to give the injected function the freedom of arguments.

On the other hand I dont really care about the arguments and return type, the wrapper should simply run the injected function as is and perform some timings and then return whatever the injected function is suppose to return.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Montaldo
  • 863
  • 8
  • 16
  • It's essentially treating the function as a first class object (which C++ allows) and as long as the return type is correct it's no different than any other return. – Dylan Lawrence Feb 19 '16 at 16:53

2 Answers2

2

With C++11 you can use variable template parameters:

class TimerFoo {
public:
   template <class Foo, class... Args> TimerFoo(Foo foo, Args... args) {
      // ... start timer
      foo(args...);
      // ... stop timer
   }
};

And use e.g.:

TimerFoo tf = TimerFoo(foo, 1, 2, 3);

Ofcourse you need some field in TimerFoo that will store the measured time...

Edit:

To be able to return a value of your function using this approach you could change the above code to:

#include <iostream>

using namespace std;

class TimerFoo {
public:
   template <class Foo, class... Args> auto run(Foo foo, Args... args) -> decltype(foo(args...)) {
      // ... start timer
      auto result = foo(args...);
      // ... stop timer
      return result;
   }
};


int foo(int a, int b) {
   return 2;
}

int main() {
   TimerFoo tf;
   cout << tf.run(foo, 1, 2) << endl; // output: 2
}
W.F.
  • 13,888
  • 2
  • 34
  • 81
  • This issue with this is that I might need the return value of foo at the callers scope. How would you solve this? – Montaldo Feb 25 '16 at 10:22
2

C++11 but why templates? You need lambda expressions:

typedef void(*TimerFunction)();

void time(TimerFunction fun) {
    // start timer
    fun();
    // stop timer
}

int fun1() { return 1; }

int fun2() { return 2; }

string fun3() { return string("Test"); }

main() {
    int ret1, ret2;
    string ret3;
    t.time([&ret1]() { ret1 = fun1(); });
    t.time([&ret2]() { ret2 = fun2(); });
    t.time([&ret3]() { ret3 = fun3(); });

}

That's the concept. For details: C++ lambda with captures as a function pointer

Community
  • 1
  • 1
ALGOholic
  • 644
  • 3
  • 7
  • 1
    Template would be to enable to run a function even if at the implementation of the timer you don't know how many parameters function takes... – W.F. Feb 19 '16 at 17:20
  • 1
    Well - lambdas could do that as well (with the technical details as previously), e.g. t.time([&ret1, &param1, &param2, &param3]() { ret1 = fun4(param1, param2, param3); }); or t.time([&ret1]() { ret1 = fun4(1,2,3); }); It amounts to the same thing in the end, a new function is either generated based on template or lambda. C++ is getting messy because all the things can be done in 4-5 ways ;) – ALGOholic Feb 19 '16 at 20:24
  • You're right in C++ everything can be done in various ways, but I wouldn't treat it as something bad... It actually shows a strength of the language... PS. I believe your code could be even simplified to t.time([&]() { ret1 = fun4(param1, param2, param3); }); – W.F. Feb 19 '16 at 21:53
  • Well - depends what you wanna capture. With & it doesn't matter so much cause it will become invalid once containing block exits anyway. But with capture-by-value you could capture only selected let's say smart pointers. Probably don't wanna capture in excess of what's needed. PS. If/when C++ becomes like JavaScript (where you can implement classes/objects themselves in so many ways) it'll hardly be an advantage. In general I like convention-based programming but some basic conventions should be enforced by the language. – ALGOholic Feb 20 '16 at 04:34