将泛型函数及其参数传递给元函数

Pass generic function and its parameters to meta-function

本文关键字:函数 参数传递 泛型      更新时间:2023-10-16

首先,我觉得这肯定已经在某个地方被问过了,但我所有的搜索都被证明是徒劳的。如果这确实是某处的重复,我提前道歉。

我正在尝试对 OpenCV 中的一堆函数进行大规模基准测试,为此我想编写一个小的元函数,该函数运行该函数,其参数(根据传递的函数而变化(并基本上设置计时并在循环中运行函数。

由于我计划稍后也将lambda传递给元函数(以基准测试函数的组合(,因此我想使用std::function

这是我在阅读参数包说明后想出的代码:

template<typename ...Ts> 
void run_test(std::string test_name, int num_repeats, std::function<void(Ts...)> f, Ts... fargs)
{
std::cout << std::endl << "Starting " << test_name << std::endl;
start_timing(test_name);
for(int i=0; i<num_repeats; i++)
{
f(fargs...);
}
end_timing(num_repeats);
}

如您所见,功能已降至最低。start_timingend_timing是超出本问题范围的简单帮助程序函数。

在我的主要情况下,我打电话:

// im_in defined and loaded elsewhere
cv::Mat out(im_in.size(), CV_8U);
run_test(
"erode 3x3",
100,
cv::erode,
im_in, out, cv::Mat::ones(3,3,CV_8U)
);

现在,如果我尝试编译它,我会得到:

error: no matching function for call to 'run_test(const char [10], const int&, void (&)(cv::InputArray, cv::OutputArray, cv::InputArray, cv::Point, int, int, const Scalar&), cv::Mat&, cv::Mat&, cv::MatExpr)'
);
^
note: candidate: template<class ... Ts> void run_test(std::__cxx11::string, int, std::function<void(Ts ...)>, Ts ...)
void run_test(std::string test_name, int num_repeats, std::function<void(Ts...)> f, Ts... fargs)
^~~~~~~~
note:   template argument deduction/substitution failed:
note:   mismatched types 'std::function<void(Ts ...)>' and 'void (*)(cv::InputArray, cv::OutputArray, cv::InputArray, cv::Point, int, int, const Scalar&) {aka void (*)(const cv::_InputArray&, const cv::_OutputArray&, const cv::_InputArray&, cv::Point_<int>, int, int, const cv::Scalar_<double>&)}'
);

那么......我做错了什么?为什么它存在类型不匹配,而不是像我预期的那样从参数列表中推断出Ts...中的类型?


更新:

写完上面的最后一个问题后,我意识到推断std::functionTs...可能有问题,因为参数在实际参数列表之前扩展到Ts...。因此,我按如下方式更改了代码(本质上,我将f参数移到了末尾(:

void run_test(std::string test_name, int num_repeats, Ts... fargs, std::function<void(Ts...)> f)
{ ... }

当然,我也相应地调整了函数调用:

cv::Mat out(im_in.size(), CV_8U);
run_test(
"erode 3x3",
NUM_REPEATS,
im_in, out, cv::Mat::ones(3,3,CV_8U),
cv::erode, // <<<<<<<<<<<<<<<<<<<<<<<
);

现在,如果我编译,我得到一个不同的错误:

error: expected primary-expression before ')' token );

误差的变化使我认为参数的顺序确实很重要。但是,这是正确的吗?如果是这样,我做错了什么?

>更新2:在编写第一次更新时,我突然意识到,我假设f可以获取函数并将其转换为std::function可能是错误的。经过快速研究,似乎确实如此。

我尝试用auto fauto& f替换std::function<void(Ts...)> f(并在启用C++14的情况下进行编译(,但expected primary-expression错误仍然存在。

对于我所能研究的所有内容,我找不到一种方法,允许我只传递依赖于编译器的函数来找出类型。 我正在考虑调整此处显示的 C++17 的std::apply函数实现,在invoke调用周围添加我的定时循环,但我不理解该代码,因此出错的可能性很高。

您可能会摆脱std::function(这会增加开销 BTW(,并且也对f使用通用:

template<typename F, typename ...Ts> 
void run_test(std::string test_name, int num_repeats, F f, const Ts&... fargs)
{
std::cout << std::endl << "Starting " << test_name << std::endl;
start_timing(test_name);
for(int i=0; i<num_repeats; i++)
{
f(fargs...);
}
end_timing(num_repeats);
}

我意识到它可能在推断 Ts... 对于 std::function 时存在问题

因为cv::erode不是一个std::functionTs...不能从中推导出来,而是从额外的参数中推导出来。

您的问题是cv::erode有额外的(默认(参数。 因此,您无法使用从参数推导Ts...创建std::function<Ts...>

要绕过该问题,您可以改用 lambda:

run_test(
"erode 3x3",
100,
[&](){ cv::erode(im_in, out, cv::Mat::ones(3,3,CV_8U)); }
);

错误expected primary-expression before ')'是因为您在最后一个参数后留下了,

参数包必须始终是函数的最后一个参数

你可能应该试试这个

template<typename Callable, typename ...Ts> 
void run_test(std::string test_name, int num_repeats, Callable func, Ts&& ... fargs)
{
std::cout << std::endl << "Starting " << test_name << std::endl;
start_timing(test_name);
for(int i=0; i<num_repeats; i++)
{
func(std::forward<Ts>(fargs)...);
}
end_timing(num_repeats);
}