【问题标题】:Wrap c++11 std::function in another std::function?将 c++11 std::function 包装在另一个 std::function 中?
【发布时间】:2016-01-27 01:44:54
【问题描述】:

我想将 std::bind() 或 lambda 的结果包装在一个帮助函数中,该函数跟踪对该函数的调用的执行时间。我想要一个通用的解决方案,它可以使用任意数量的参数(和类方法)并且兼容 c++11。

我的意图是获取包装函数并将其传递给 boost::signals2::signal,因此生成的函数对象需要与原始函数的签名相同。

我基本上是在寻找一些像这样工作的神奇类或函数Wrapper

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

计算打印 42 需要多长时间。

谢谢!

【问题讨论】:

  • 您想如何处理执行时间?直接打印到标准输出?
  • 你可以做something like this,并将大部分艰苦的工作委托给std::function。
  • 使用 C++11 和 boost.signals2:coliru
  • @melak47 啊-实际上并没有查看您的样本。我的回答中的 RAII 技巧避免了 void 返回类型的特殊情况!

标签: c++ c++11 boost boost-signals2


【解决方案1】:

如果是关于性能,我强烈建议不要双重包装函数。

你可以不用这些:

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

观看现场演示:

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

打印(例如):

 -- (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

更新:c++11 版本

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

【讨论】:

  • 实际上,我比我更喜欢你的,因为你不需要 timed 中的模板参数。将来会注意到这一点,std::forwarddecltype 是与可变参数模板的良好组合。
  • 我忘了在我的帖子中提到我在 c++11 上。这是一个很好的解决方案,但返回 auto 需要 c++14。如果我将您的现场演示更改为 std=c++11,它将不再编译。
  • @moof2k 没问题。那么它会稍微冗长一些。今晚晚些时候发布
  • @moof2k 好了,晚饭前完成,匆匆忙忙:Live On Coliru
【解决方案2】:

我相信你想做的事情可以用可变参数模板来解决。

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

您基本上可以将参数列表从外部 std::function “转发”到内部。

编辑:

下面,我添加了一个使用可变参数模板概念的最小工作示例。在main(...) 中,一个lambda 函数被包装到另一个std::function 对象中,使用内部lambda 函数的指定参数。这是通过将要测量的函数作为参数传递给模板化函数measureTimeWrapper 来完成的。它返回一个与传入的函数具有相同签名的函数(假设您在 measureTimeWrapper 的模板参数中正确定义了该 lambda 的参数列表)。

测量运行时间的函数只是坐在这里并等待其参数定义的毫秒数。除此之外,它根本不关心时间测量。这是由包装函数完成的。

注意,内部函数的返回值是这样丢失的;如果你想保留它,你可能想改变返回值的方式(可能是一个结构,包含测量的时间和 real 返回值)。

记得至少用-std=c++11 编译你的代码。

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

【讨论】:

  • 在 SO 上不需要仅链接的答案。此外,cplusplus.com 并未被广泛视为主要资源。
  • @sehe 我明白了这一点,并添加了一个工作代码示例和更多解释。感谢您的提示。不过,我不明白为什么 cplusplus.com 不应该是一个有效的参考。
  • 我明白了,谢谢。不过,从来没有遇到过任何问题。
  • 这很好,可以在 c++11 中使用,但不符合我的 boost::signals2 要求。不过,如果你稍微改变它(返回 void 而不是时间加倍),那么效果很好。不幸的是,这并不能推广到任何返回类型,但无论如何我都不关心信号。谢谢!
【解决方案3】:
#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;
}

"function tracked"part 替换为您想要的任何跟踪逻辑(计时、缓存等)

这需要c++14

【讨论】:

  • 这是最棒的答案,但遗憾的是我在一个还不支持 c++14 的平台上。谢谢!
猜你喜欢
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多