Search code examples
c++c++11boostboost-signals2

Wrap c++11 std::function in another std::function?


I'd like to wrap the result of a std::bind() or a lambda in a helper function that tracks the execution time of calls to the function. I'd like a generalized solution that will work with any number of parameters (and class methods) and is c++11 compatible.

My intent is to take the wrapped function and pass it to a boost::signals2::signal so the resulting function object needs to be identical in signature to the original function.

I'm basically looking for some magical class or function Wrapper that works like this:

std::function<void(int)> f = [](int x) {
    std::cerr << x << std::endl;
};

boost::signals2::signal<void(int)> x_signal;
x_signal.connect(Wrapper<void(int)>(f));
x_signal(42);

that would time how long it took to print 42.

Thanks!


Solution

  • I believe what you want to do can be solved with variadic templates.

    http://www.cplusplus.com/articles/EhvU7k9E/

    You can basically "forward" the argument list from your outer std::function to the inner.

    EDIT:

    Below, I added a minimal working example using the variadic template concept. In main(...), a lambda function is wrapped into another std::function object, using the specified parameters for the inner lambda function. This is done by passing the function to measure as a parameter to the templated function measureTimeWrapper. It returns a function with the same signature as the function passed in (given that you properly define that lambda's parameter list in the template argument of measureTimeWrapper).

    The function who's running time is measured just sits here and waits for a number of milliseconds defined by its parameter. Other than that, it is not at all concerned with time measuring. That is done by the wrapper function.

    Note that the return value of the inner function is lost this way; you might want to change the way values are returned (maybe as a struct, containing the measured time and the real return value) if you want to keep it.

    Remember to compile your code with -std=c++11 at least.

    #include <iostream>
    #include <cstdlib>
    #include <functional>
    #include <chrono>
    #include <thread>
    
    
    template<typename T, typename... Args>
    std::function<double(Args...)> measureTimeWrapper(std::function<T> fncFunctionToMeasure) {
      return [fncFunctionToMeasure](Args... args) -> double {
        auto tsStart = std::chrono::steady_clock::now();
        fncFunctionToMeasure(args...);
        auto tsEnd = std::chrono::steady_clock::now();
    
        std::chrono::duration<double> durTimeTaken = tsEnd - tsStart;
    
        return durTimeTaken.count();
      };
    }
    
    int main(int argc, char** argv) {
      std::function<double(int)> fncMeasured = measureTimeWrapper<void(int), int>([](int nParameter) {
          std::cout << "Process function running" << std::endl;
    
          std::chrono::milliseconds tsTime(nParameter); // Milliseconds
          std::this_thread::sleep_for(tsTime);
        });
    
      std::cout << "Time taken: " << fncMeasured(500) << " sec" << std::endl;
    
      return EXIT_SUCCESS;
    }