通过参数传递任何函数



解释

你好,我想创建一个可以执行任何类型函数的函数,在执行结束时指示所花费的时间。被调用的函数可以有返回值,也可以有0个或多个任何类型的参数。

调用函数必须打印如下内容:

Running "myFunction" .....  
Done ! (5210ms)

基本上,我想创建一个函数,通过在调用前后添加代码来调用作为参数传递的任何类型的函数。

我所做的

现在我是这样做的。

调用函数:

template <typename T>
T callFunctionPrintTime(std::string fnName, std::function<T()> fn) {
std::cout << ">> Running " << fnName << " ... " << std::endl;
auto t1 = std::chrono::high_resolution_clock::now();
//Call to the target function
T retVal = fn();
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::cout << "Done ! (" << duration << " ms)" << std::endl;
return retVal;
}

主要

int main()
{
//Store the function to call
std::function<unsigned long()> fn = []() {
return myFunction(15, 10000);
};
//Use of the function we are interested in
auto i = callFunctionPrintTime("myFunction", fn);
//The return value of myFunction can be used in the rest of the program.
std::cout << "i: " << i << std::endl;
}

myFunction
这个函数无关紧要,它可以是任何东西。

在这里,我们在给定的最大时间或最大循环次数内执行while循环,并检索执行的循环次数

unsigned long myFunction(long maxMs, unsigned long maxI) {
unsigned long i = 0;
auto tStart = std::chrono::high_resolution_clock::now();
while (maxMs > (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - tStart).count()) &&
maxI > i) {
i++;
}
return i;
}

问题

对你来说最好的方法是什么?我对我的代码不满意
我不确定是否使用了正确的方式通过参数传递任何类型的函数
此外,由于使用lambda表达式存储函数,我无法检索被调用函数的名称。所以我必须通过参数传递它的名称。

我很确定什么是最好的没有单一的答案,但这是一个小的改进,因为它有点通用。

#include <chrono>
#include <iostream>
#include <string>
#include <type_traits>
// enable it for invocables with any type of arguments
template <class Func, class... Args,
std::enable_if_t<std::is_invocable_v<Func, Args...>, int> = 0>
decltype(auto) callFunctionPrintTime(std::string fnName, Func fn, Args&&... args)
{
std::cout << ">> Running " << fnName << " ... " << std::endl;
auto t1 = std::chrono::high_resolution_clock::now();
//Call to the target function by forwarding the arguments to it
decltype(auto) retVal = fn(std::forward<Args>(args)...);
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = 
std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::cout << "Done ! (" << duration << " ms)" << std::endl;
return retVal;
}

或者,如果你不打算为不可调用的对象进行重载(这似乎很明显,当我考虑时你不会),你可以使用static_assert而不是SFINAE:

template <class Func, class... Args>
decltype(auto) callFunctionPrintTime(std::string fnName, Func fn, Args&&... args)
{
static_assert(std::is_invocable_v<Func, Args...>, "must be invocable");
//...

测试用途:

int& a_func(int i) {
static int rv = 0;
rv += i;
return rv;
}

int main() {
int& ref = callFunctionPrintTime("a_func 1", a_func, 10);

std::cout << ref << 'n';  // prints 10

ref += 20;
callFunctionPrintTime("a_func 2", a_func, 100);
std::cout << ref << 'n';  // prints 130 (10 + 20 + 100)
}

或者调用myFunction:的一些替代方案

std::function<unsigned long()> fn = []() { return myFunction(15, 100000); };
std::cout << callFunctionPrintTime("myFunction", fn);
std::cout << callFunctionPrintTime("myFunction",
[]() { return myFunction(15, 100000); });
std::cout << callFunctionPrintTime("myFunction", myFunction, 15, 100000);

一些有用的链接:decltype(auto)std::enable_if_tstd::is_invocable_v、SFINAE

主要思想是正确的。有一些细节可能需要改进:

template <typename Func, typename ... Ts>
decltype(auto) callFunctionPrintTime(std::string_view fnName, Func&& f, Ts&&... args) {
static_assert(std::is_invocable_v<Func&&, Ts&&...>); // Possibly SFINAE instead.
std::cout << ">> Running " << fnName << " ... " << std::endl;
struct Finally {
std::chrono::time_point<std::chrono::high_resolution_clock> t1 =
std::chrono::high_resolution_clock::now();
~Finally() {
auto t2 = std::chrono::high_resolution_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::cout << "Done ! (" << duration << " ms)" << std::endl;
}
} finally;
return std::invoke(std::forward<Func>(f), std::forward<Ts>(args)...);
}

现在:

  • 处理void返回类型(不需要特殊化)
  • 在出现异常的情况下也要记录(您可以使用std::uncaught_exceptions或try/catch块进一步将异常与正常路径分离)
  • 用它的参数处理任何可调用程序

对于自动命名,我们必须依赖MACRO:

#define CallFunctionPrintTime(F, ...) callFunctionPrintTime(#F, F __VA_OPT__(,) __VA_ARGS__)

演示

最新更新