对任何其他任意函数及其结果进行计时的函数 return

A function to time any other arbitrary function and return its results

我正在尝试编写一个函数,它接受任何其他任意函数作为输入并对其计时,然后 returns 该函数的结果。我已经用了几个小时了,我觉得我已经很接近了,但我仍然不太清楚如何让它编译。

这是我目前拥有的:

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

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

这里的问题是 T 在你的函数中是不可推导的。您分配给 return 的值不参与模板参数推导。要按原样使用它,您需要使用

指定 return 类型
int output = GetRuntime<int>("DoSomething", DoSomething, 42, 42.42);
                        ^^^ specify T is an int

但是我们可以通过对函数的 return 类型使用 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;
}

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

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

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 {
    // ...
}

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

(这都是 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";

对于非 void 函数,您使用结构化绑定:

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

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

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