【问题标题】:A function to time any other arbitrary function and return its results对任何其他任意函数计时并返回其结果的函数
【发布时间】:2019-08-13 13:58:40
【问题描述】:

我正在尝试编写一个函数,该函数接受任何其他任意函数作为输入并对其计时,然后返回该函数的结果。我已经研究了几个小时,并且认为我已经很接近了,但我仍然无法弄清楚如何编译它。

这是我目前所拥有的:

// Some arbitrary function to pass to the timer
int DoSomething(int x, double y)
{
    // Does something with x and y
    return 0;
}

// Function used to time another function and return its result
template <typename T, typename Function, typename... Args>
T GetRuntime(const std::string& name, const Function& function, Args&&... args)
{
    std::cout << name << " started..." << std::endl;
    auto start = std::chrono::high_resolution_clock::now();

    T result = function(std::forward<Args>(args)...);

    auto stop = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
    std::cout << name << " complete - duration: " << duration.count() << " milliseconds." << std::endl;

    return result;
}

int main()
{
    // Doesn't compile
    int output = GetRuntime("DoSomething", DoSomething, 42, 42.42);
}

我是否以正确的方式处理这个问题?如果是这样,我需要改变什么才能使这项工作?如果不是,有什么更好的方法来解决这个问题?

【问题讨论】:

  • 您可以通过创建一个作用域计时器稍有不同,并将其添加到您要调用的函数的顶部。构造函数将启动计时器,析构函数将停止它并打印持续时间。您需要在函数开头编写的唯一代码是 ScopedTimer timer()

标签: c++ templates generic-programming


【解决方案1】:

这里的问题是 T 在你的函数中是不可推导的。您分配返回的值不参与模板参数扣除。要按原样使用它,您需要使用指定返回类型

int output = GetRuntime<int>("DoSomething", DoSomething, 42, 42.42);
                        ^^^ specify T is an int

但我们可以通过将auto 用作函数的返回类型来改善这一点。使用它将函数变为

template <typename Function, typename... Args>
auto GetRuntime(const std::string& name, const Function& function, Args&&... args)
{
    std::cout << name << " started..." << std::endl;
    auto start = std::chrono::high_resolution_clock::now();

    auto result = function(std::forward<Args>(args)...);

    auto stop = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
    std::cout << name << " complete - duration: " << duration.count() << " milliseconds." << std::endl;

    return result;
}

【讨论】:

  • 如果function 返回void 将不起作用。将所有计时器的东西移到帮助类的构造函数和析构函数中。在GetRuntime 中构造辅助类,如果function 返回voidreturn function(std::forward&lt;Args&gt;(args)...); 也将起作用。
  • @SamVarshavchik 好点。如果这是他们想要的,我将把它留给 OP 来实施。
  • 我想我可以应付。总的来说,这是一个很好的答案。我试了一下,现在可以编译了。将其标记为已接受。 :)
  • @tjwrona1992 听起来不错,谢谢。如果需要帮助使其更通用,只需提出后续问题。
  • 似乎即使在进行了建议的更改以使用辅助类之后,它仍然不适用于 void 函数。我进行了更改并尝试传入一个 void 函数并得到“致命错误 C1001:编译器中发生内部错误”...感谢 Microsoft 提供完全无用的错误消息!
【解决方案2】:

编译器无法推断T。这是一个模板参数,但您的参数中没有任何内容可以推导出它。

但是,您可以使用std::invoke_result 来获取函数的返回类型:

template <typename Function, typename... Args, typename R = std::invoke_result_t<Function, Args...>>
auto GetRuntime(const std::string& name, const Function& function, Args&&... args) -> R {
    // ...
}

【讨论】:

  • 为什么R在这里是一个模板参数,而不是在尾随返回类型中直接使用std::invoke_result_t
  • @KonradRudolph 仅仅是因为 OP 似乎想要使用名称而不是 auto。另外,SFINAE
  • @GuillaumeRacicot 那只是因为我不知道我在做什么哈哈哈
【解决方案3】:

NathanOliver 已经给出了正确答案。这只是关于如何使函数更通用的答案。它也适用于void 函数,并且不进行任何日志记录。它返回一个元组,其中包含传递函数的持续时间和返回值。如果传递的函数返回void,则直接返回持续时间(无元组)。

(这都是 C++17。)

// Need these includes in addition to what you already include.
#include <tuple>
#include <type_traits>

template <typename Function, typename... Args>
auto GetRuntime(const Function& function, Args&&... args)
{
    auto start = std::chrono::high_resolution_clock::now();

    if constexpr (std::is_same_v<decltype(function(args...)), void>) {
        function(std::forward<Args>(args)...);
        return std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::high_resolution_clock::now() - start);
    } else {
        auto&& func_result{function(std::forward<Args>(args)...)};
        return std::make_tuple(
            std::chrono::duration_cast<std::chrono::milliseconds>(
                std::chrono::high_resolution_clock::now() - start),
                std::forward<decltype(function(args...))>(func_result));
    }
}

您可以直接使用它来获取持续时间:

auto duration = GetRunTime(void_func, arg1, arg2);
cout << "Took " << duration.count() << "ms.\n";

对于非空函数,您可以使用结构化绑定:

auto [duration, int_val] = GetRunTime(int_func, arg1, arg2);
cout << "Took " << duration.count() << "ms and returned " << int_val << '\n';

您的原始日志记录功能可以作为GetRuntime() 的包装器来实现。日志记录函数的返回类型将与传递函数的返回类型相同:

template <typename Function, typename... Args>
auto LogRuntime(const std::string& name, const Function& function, Args&&... args)
{
    std::cout << name << " starting..." << std::endl;
    auto result = GetRuntime(function, std::forward<Args>(args)...);
    std::cout << name << " complete - duration: ";
    constexpr auto is_void = std::is_same_v<decltype(function(args...)), void>;
    if constexpr (is_void) {
        std::cout << result.count();
    } else {
        std::cout << std::get<0>(result).count();
    }
    std::cout << " milliseconds.\n";
    if constexpr (!is_void) {
        return std::get<1>(result);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-27
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 2018-02-09
    • 1970-01-01
    相关资源
    最近更新 更多