简体   繁体   中英

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!

If it's about performance, I strongly suggest not to doubly wrap functions.

You can do without those:

template <typename Caption, typename F>
auto timed(Caption const& task, F&& f) {
    return [f=std::forward<F>(f), task](auto&&... args) {
        using namespace std::chrono;

        struct measure {
            high_resolution_clock::time_point start;
            Caption const& task;
            ~measure() { std::cout << " -- (" << task << " completed in " << duration_cast<microseconds>(high_resolution_clock::now() - start).count() << "µs)\n"; }
        } timing { high_resolution_clock::now(), task };

        return f(std::forward<decltype(args)>(args)...);
    };
}

See live demo:

Live On Coliru

#include <chrono>
#include <iostream>

template <typename Caption, typename F>
auto timed(Caption const& task, F&& f) {
    return [f=std::forward<F>(f), task](auto&&... args) {
        using namespace std::chrono;

        struct measure {
            high_resolution_clock::time_point start;
            Caption const& task;
            ~measure() { std::cout << " -- (" << task << " completed in " << duration_cast<microseconds>(high_resolution_clock::now() - start).count() << "µs)\n"; }
        } timing { high_resolution_clock::now(), task };

        return f(std::forward<decltype(args)>(args)...);
    };
}

#include <thread>

int main() {
    using namespace std;
    auto f = timed("IO", [] { cout << "hello world\n"; return 42; });
    auto g = timed("Sleep", [](int i) { this_thread::sleep_for(chrono::seconds(i)); });

    g(1);
    f();
    g(2);

    std::function<int()> f_wrapped = f;
    return f_wrapped();
}

Prints (eg):

 -- (Sleep completed in 1000188µs)
hello world
 -- (IO completed in 2µs)
 -- (Sleep completed in 2000126µs)
hello world
 -- (IO completed in 1µs)
exitcode: 42

UPDATE: c++11 version

Live On Coliru

#include <chrono>
#include <iostream>

namespace detail {

    template <typename F>
    struct timed_impl {
        std::string _caption;
        F _f;

        timed_impl(std::string const& task, F f) 
            : _caption(task), _f(std::move(f)) { }

        template <typename... Args>
        auto operator()(Args&&... args) const -> decltype(_f(std::forward<Args>(args)...))
        {
            using namespace std::chrono;

            struct measure {
                high_resolution_clock::time_point start;
                std::string const& task;
                ~measure() { std::cout << " -- (" << task << " completed in " << duration_cast<microseconds>(high_resolution_clock::now() - start).count() << "µs)\n"; }
            } timing { high_resolution_clock::now(), _caption };

            return _f(std::forward<decltype(args)>(args)...);
        }
    };
}

template <typename F>
detail::timed_impl<F> timed(std::string const& task, F&& f) {
    return { task, std::forward<F>(f) };
}

#include <thread>

int main() {
    using namespace std;
    auto f = timed("IO", [] { cout << "hello world\n"; return 42; });
    auto g = timed("Sleep", [](int i) { this_thread::sleep_for(chrono::seconds(i)); });

    g(1);
    f();
    g(2);

    std::function<int()> f_wrapped = f;
    return f_wrapped();
}

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;
}
#include <iostream>
#include <functional>

template<typename Signature>
std::function<Signature>    Wrapper(std::function<Signature> func)
{
  return [func](auto... args)
    {
      std::cout << "function tracked" << std::endl;
      return func(args...);
    };
}

int     lol(const std::string& str)
{
  std::cout << str << std::endl;

  return 42;
}

int     main(void)
{
  auto wrapped = Wrapper<int(const std::string&)>(lol);

  std::cout << wrapped("Hello") << std::endl;
}

Replace the "function tracked" part with whatever tracking logic you want (timing, cache, etc.)

This requires c++14 though

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM